├── .devcontainer.json
├── .github
├── CODEOWNERS
└── workflows
│ └── validate_on_platforms.yml
├── .gitignore
├── .sqlfluff
├── .sqlfluffignore
├── .vscode
└── extensions.json
├── Dockerfile
├── LICENSE
├── README.md
├── RELEASE.md
├── dbt-completion.bash
├── dbt_project.yml
├── etc
├── dbdiagram_definition.txt
└── jaffle_shop_erd.png
├── images
├── dbt_full_deploy_commands.png
├── dbt_performance.png
└── open_in_codespaces.png
├── models
├── customers.sql
├── docs.md
├── orders.sql
├── overview.md
├── schema.yml
└── staging
│ ├── schema.yml
│ ├── stg_customers.sql
│ ├── stg_orders.sql
│ └── stg_payments.sql
├── profiles.yml
├── requirements-dev.txt
├── requirements.in
├── requirements.txt
└── seeds
├── .gitkeep
├── raw_customers.csv
├── raw_orders.csv
└── raw_payments.csv
/.devcontainer.json:
--------------------------------------------------------------------------------
1 | // Update the VARIANT arg in docker-compose.yml to pick a Python version: 3, 3.8, 3.7, 3.6
2 | {
3 | "name": "dbt",
4 | "dockerFile" : "Dockerfile",
5 | "settings": {
6 | "terminal.integrated.defaultProfile.linux#": "/bin/sh",
7 | "files.associations": {
8 | "*.sql": "jinja-sql",
9 | "*.sqlfluff": "ini",
10 | "*.yml": "yaml",
11 | "**/target/**": ""
12 | },
13 | "sql.linter.executablePath": "/usr/local/bin/sqlfluff",
14 | "sql.format.enable": true,
15 | "sql.linter.run": "onType",
16 | "files.exclude": {
17 | "**/.git": true,
18 | "**/.svn": true,
19 | "**/.hg": true,
20 | "**/CVS": true,
21 | "**/.DS_Store": true,
22 | "**/__pycache__": true
23 | },
24 | "findrelated.workspaceRulesets": [
25 | {
26 | "name": "sql",
27 | "rules": [
28 | {
29 | "pattern": "^(.*/)?models/(.*/)?(.+\\.sql)$",
30 | "locators": [
31 | "**/compiled/**/$3"
32 | ]
33 | },
34 | {
35 | "pattern": "^(.*/)?compiled/(.*/)?(.+\\.sql)$",
36 | "locators": [
37 | "**/run/**/$3"
38 | ]
39 | },
40 | {
41 | "pattern": "^(.*/)?run/(.*/)?(.+\\.sql)$",
42 | "locators": [
43 | "**/models/**/$3"
44 | ]
45 | }
46 | ]
47 | }
48 | ],
49 | "findrelated.applyRulesets": [
50 | "sql"
51 | ],
52 | "findrelated.applyWorkspaceRulesets": [
53 | "sql"
54 | ],
55 | "workbench.editor.highlightModifiedTabs": true,
56 | "workbench.editor.labelFormat": "medium",
57 | "workbench.editor.revealIfOpen": true,
58 | "editor.rulers": [
59 | 99
60 | ],
61 | "yaml.schemas": {
62 | "https://raw.githubusercontent.com/dbt-labs/dbt-jsonschema/main/schemas/dbt_yml_files.json": [
63 | "/*.yml",
64 | "!profiles.yml",
65 | "!dbt_project.yml",
66 | "!packages.yml",
67 | "!selectors.yml",
68 | "!profile_template.yml"
69 | ],
70 | "https://raw.githubusercontent.com/dbt-labs/dbt-jsonschema/main/schemas/dbt_project.json": [
71 | "dbt_project.yml"
72 | ],
73 | "https://raw.githubusercontent.com/dbt-labs/dbt-jsonschema/main/schemas/selectors.json": [
74 | "selectors.yml"
75 | ],
76 | "https://raw.githubusercontent.com/dbt-labs/dbt-jsonschema/main/schemas/packages.json": [
77 | "packages.yml"
78 | ]
79 | }
80 | },
81 | "extensions": [
82 | "bastienboutonnet.vscode-dbt",
83 | "dorzey.vscode-sqlfluff",
84 | "editorconfig.editorconfig",
85 | "amodio.find-related",
86 | "ms-azuretools.vscode-docker",
87 | "ms-python.python",
88 | "visualstudioexptteam.vscodeintellicode",
89 | "samuelcolvin.jinjahtml",
90 | "redhat.vscode-yaml"
91 | ],
92 |
93 | "remoteUser": "vscode"
94 | }
95 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @dbt-labs/dx
2 |
--------------------------------------------------------------------------------
/.github/workflows/validate_on_platforms.yml:
--------------------------------------------------------------------------------
1 | name: Platform Validation
2 |
3 | on:
4 | push:
5 | branches: [duckdb]
6 | pull_request:
7 | branches: [duckdb]
8 |
9 | jobs:
10 | build-macos:
11 | name: Validate Getting Started works on macOS
12 | runs-on: macos-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - name: Set up Python
16 | uses: actions/setup-python@v4
17 | with:
18 | python-version: "3.12.x"
19 | - name: Full Deploy Commands
20 | run: |
21 | python3 -m venv venv
22 | source venv/bin/activate
23 | venv/bin/python3 -m pip install --upgrade pip
24 | python3 -m pip install -r requirements.txt
25 | source venv/bin/activate
26 | which dbt
27 | dbt --version
28 | dbt debug
29 | dbt build
30 | dbt docs generate
31 | duckcli jaffle_shop.duckdb -e "select * from customers where customer_id = 42"
32 | shell: bash
33 |
34 | build-linux:
35 | name: Validate Getting Started works on linux
36 | runs-on: ubuntu-latest
37 | steps:
38 | - uses: actions/checkout@v3
39 | - name: Set up Python
40 | uses: actions/setup-python@v4
41 | with:
42 | python-version: "3.12.x"
43 | - name: Full Deploy Commands
44 | run: |
45 | python3 -m venv venv
46 | source venv/bin/activate
47 | venv/bin/python3 -m pip install --upgrade pip
48 | python3 -m pip install -r requirements.txt
49 | source venv/bin/activate
50 | which dbt
51 | dbt --version
52 | dbt debug
53 | dbt build
54 | dbt docs generate
55 | duckcli jaffle_shop.duckdb -e "select * from customers where customer_id = 42"
56 | shell: bash
57 |
58 | build-linux-powershell-core:
59 | name: Validate Getting Started works on Linux PowerShell Core
60 | runs-on: ubuntu-latest
61 | steps:
62 | - uses: actions/checkout@v3
63 | - name: Set up Python
64 | uses: actions/setup-python@v4
65 | with:
66 | python-version: "3.12.x"
67 | - name: Full Deploy Commands
68 | run: |
69 | python3 -m venv venv
70 | venv/bin/Activate.ps1
71 | venv/bin/python3 -m pip install --upgrade pip
72 | python3 -m pip install -r requirements.txt
73 | venv/bin/Activate.ps1
74 | Get-Command dbt
75 | dbt --version
76 | dbt debug
77 | dbt build
78 | dbt docs generate
79 | duckcli jaffle_shop.duckdb -e "select * from customers where customer_id = 42"
80 | shell: pwsh
81 |
82 | build-windows-powershell:
83 | name: Validate Getting Started works on Windows PowerShell
84 | runs-on: windows-latest
85 | steps:
86 | - uses: actions/checkout@v3
87 | - name: Set up Python
88 | uses: actions/setup-python@v4
89 | with:
90 | python-version: "3.12.x"
91 | - name: Full Deploy Commands
92 | run: |
93 | python -m venv venv
94 | venv\Scripts\Activate.ps1
95 | python -m pip install --upgrade pip
96 | python -m pip install -r requirements.txt
97 | venv\Scripts\Activate.ps1
98 | Get-Command dbt
99 | dbt --version
100 | dbt debug
101 | dbt build
102 | dbt docs generate
103 | duckcli jaffle_shop.duckdb -e "select * from customers where customer_id = 42"
104 | shell: powershell
105 |
106 | build-windows-cmd:
107 | name: Validate Getting Started works on Windows cmd.exe
108 | runs-on: windows-latest
109 | steps:
110 | - uses: actions/checkout@v3
111 | - name: Set up Python
112 | uses: actions/setup-python@v4
113 | with:
114 | python-version: "3.12.x"
115 | - name: Full Deploy Commands
116 | run: |
117 | python -m venv venv
118 | venv\Scripts\activate.bat
119 | python -m pip install --upgrade pip
120 | python -m pip install -r requirements.txt
121 | venv\Scripts\activate.bat
122 | where dbt
123 | dbt --version
124 | dbt debug
125 | dbt build
126 | dbt docs generate
127 | duckcli jaffle_shop.duckdb -e "select * from customers where customer_id = 42"
128 | shell: cmd
129 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | target/
3 | dbt_packages/
4 | dbt_internal_packages/
5 | fs_internal_packages/
6 | dbt_modules/
7 | logs/
8 | **/.DS_Store
9 | .user.yml
10 | venv/
11 | env/
12 | **/*.duckdb
13 | **/*.duckdb.wal
14 |
--------------------------------------------------------------------------------
/.sqlfluff:
--------------------------------------------------------------------------------
1 | [sqlfluff]
2 |
3 | dialect = duckdb
4 |
--------------------------------------------------------------------------------
/.sqlfluffignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbt-labs/jaffle_shop_duckdb/def2ee2ada92b6409b7ad5031bee9fdda9ec4877/.sqlfluffignore
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4 |
5 | // List of extensions which should be recommended for users of this workspace.
6 | "recommendations": [
7 | "mechatroner.rainbow-csv",
8 | "yzhang.markdown-all-in-one",
9 | "redhat.vscode-yaml"
10 | ],
11 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
12 | "unwantedRecommendations": [
13 | "innoverio.vscode-dbt-power-user",
14 |
15 | ]
16 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM mcr.microsoft.com/vscode/devcontainers/python:3.9
2 |
3 | ARG USER_UID=1000
4 | ARG USER_GID=$USER_UID
5 |
6 | RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then groupmod --gid $USER_GID vscode && usermod --uid $USER_UID --gid $USER_GID vscode; fi
7 |
8 | RUN pwd
9 | RUN ls
10 | COPY requirements.txt /tmp/
11 | RUN pip3 install --upgrade pip
12 | RUN pip3 install --requirement /tmp/requirements.txt
13 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Testing dbt project: `jaffle_shop`
2 |
3 | `jaffle_shop` is a fictional ecommerce store. This dbt project transforms raw data from an app database into a customers and orders model ready for analytics.
4 |
5 |
6 |
7 |
8 | ## What is this repo?
9 |
10 |
11 |
12 | What this repo _is_:
13 | - A self-contained playground dbt project, useful for testing out scripts, and communicating some of the core dbt concepts.
14 |
15 | What this repo _is not_:
16 | - A tutorial — check out the [Getting Started Tutorial](https://docs.getdbt.com/tutorial/setting-up) for that. Notably, this repo contains some anti-patterns to make it self-contained, namely the use of seeds instead of sources.
17 | - A demonstration of best practices — check out the [dbt Learn Demo](https://github.com/dbt-labs/dbt-learn-demo) repo instead. We want to keep this project as simple as possible. As such, we chose not to implement:
18 | - our standard file naming patterns (which make more sense on larger projects, rather than this five-model project)
19 | - a pull request flow
20 | - CI/CD integrations
21 | - A demonstration of using dbt for a high-complex project, or a demo of advanced features (e.g. macros, packages, hooks, operations) — we're just trying to keep things simple here!
22 |
23 |
24 |
25 |
26 |
27 |
28 | ## What's in this repo?
29 |
30 |
31 |
32 | This repo contains [seeds](https://docs.getdbt.com/docs/building-a-dbt-project/seeds) that includes some (fake) raw data from a fictional app along with some basic dbt [models](https://docs.getdbt.com/docs/building-a-dbt-project/building-models), tests, and docs for this data.
33 |
34 | The raw data consists of customers, orders, and payments, with the following entity-relationship diagram:
35 |
36 | 
37 |
38 |
39 |
40 | ## Why should I care about this repo?
41 | If you're just starting your cloud data warehouse journey and are hungry to get started with dbt before your organization officially gets a data warehouse, you should check out this repo.
42 |
43 | If you want to run 28 SQL operations with dbt in less than `1 second`, for free, and all on your local machine, you should check out this repo.
44 | 
45 |
46 | If you want an adrenaline rush from a process that used to take dbt newcomers `1 hour` and is now less than `1 minute`, you should check out this repo.
47 |
48 | 
49 |
50 | [Verified GitHub Action on dbt Performance](https://github.com/dbt-labs/jaffle_shop_duckdb/runs/7141529753?check_suite_focus=true#step:4:306)
51 |
52 | ## Running this project
53 | Prerequisities: Python >= 3.5
54 |
55 | ### Mach Speed: No explanation needed
56 |
57 | > Run `dbt` as fast as possible in a single copy and paste motion!
58 |
59 |
60 | POSIX bash/zsh
61 |
62 | ```shell
63 | git clone https://github.com/dbt-labs/jaffle_shop_duckdb.git
64 | cd jaffle_shop_duckdb
65 | python3 -m venv venv
66 | source venv/bin/activate
67 | python3 -m pip install --upgrade pip
68 | python3 -m pip install -r requirements.txt
69 | source venv/bin/activate
70 | dbt build
71 | dbt docs generate
72 | dbt docs serve
73 | ```
74 |
75 |
76 |
77 | POSIX fish
78 |
79 | ```shell
80 | git clone https://github.com/dbt-labs/jaffle_shop_duckdb.git
81 | cd jaffle_shop_duckdb
82 | python3 -m venv venv
83 | source venv/bin/activate.fish
84 | python3 -m pip install --upgrade pip
85 | python3 -m pip install -r requirements.txt
86 | source venv/bin/activate.fish
87 | dbt build
88 | dbt docs generate
89 | dbt docs serve
90 | ```
91 |
92 |
93 |
94 | POSIX csh/tcsh
95 |
96 | ```shell
97 | git clone https://github.com/dbt-labs/jaffle_shop_duckdb.git
98 | cd jaffle_shop_duckdb
99 | python3 -m venv venv
100 | source venv/bin/activate.csh
101 | python3 -m pip install --upgrade pip
102 | python3 -m pip install -r requirements.txt
103 | source venv/bin/activate.csh
104 | dbt build
105 | dbt docs generate
106 | dbt docs serve
107 | ```
108 |
109 |
110 |
111 | POSIX PowerShell Core
112 |
113 | ```shell
114 | git clone https://github.com/dbt-labs/jaffle_shop_duckdb.git
115 | cd jaffle_shop_duckdb
116 | python3 -m venv venv
117 | venv/bin/Activate.ps1
118 | python3 -m pip install --upgrade pip
119 | python3 -m pip install -r requirements.txt
120 | venv/bin/Activate.ps1
121 | dbt build
122 | dbt docs generate
123 | dbt docs serve
124 | ```
125 |
126 |
127 |
128 | Windows cmd.exe
129 |
130 | ```shell
131 | git clone https://github.com/dbt-labs/jaffle_shop_duckdb.git
132 | cd jaffle_shop_duckdb
133 | python -m venv venv
134 | venv\Scripts\activate.bat
135 | python -m pip install --upgrade pip
136 | python -m pip install -r requirements.txt
137 | venv\Scripts\activate.bat
138 | dbt build
139 | dbt docs generate
140 | dbt docs serve
141 | ```
142 |
143 |
144 |
145 | Windows PowerShell
146 |
147 | ```shell
148 | git clone https://github.com/dbt-labs/jaffle_shop_duckdb.git
149 | cd jaffle_shop_duckdb
150 | python -m venv venv
151 | venv\Scripts\Activate.ps1
152 | python -m pip install --upgrade pip
153 | python -m pip install -r requirements.txt
154 | venv\Scripts\Activate.ps1
155 | dbt build
156 | dbt docs generate
157 | dbt docs serve
158 | ```
159 |
160 |
161 |
162 | GitHub Codespaces / Dev Containers
163 |
164 | #### Steps
165 |
166 | 1. Ensure you have [Codespaces](https://github.com/features/codespaces) enabled for your GitHub organization or turned on as a beta feature if you're an individual user
167 | 2. Click the green **Code** button on near the top right of the page of this repo's homepage (you may already be on it)
168 | 3. Instead of cloning the repo like you normally would, instead select the **Codespaces** tab of the pop out, then "Create codespace on `duckdb`"
169 | 
170 | 4. Wait for codespace to boot (~1 min?)
171 | 5. Decide whether you'd like to use the Web IDE or open the codespace in your local environment
172 | 6. When the codespace opens, a Task pane will show up and call `dbt build` just to show you how it's done
173 | 7. Decide whether or not you'd like the recommended extensions installed (like **dbt Power User extension**)
174 | 8. Open up a new terminal and type:
175 | ```
176 | dbt build
177 | ```
178 | 9. Explore some of the bells and whistles (see below)
179 |
180 | If you don't have Codespaces or would like to just run the environment in a local Docker container, you can by:
181 | 1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/)
182 | 2. Install the VSCode [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension (formerly known as the "Remote - Containers" extension). Video tutorial [here](https://learn.microsoft.com/en-us/shows/beginners-series-to-dev-containers/installing-the-remote-containers-extension-2-of-8--beginners-series-to-dev-containers).
183 | 2. Clone this repo and open it in VSCode
184 | 1. First time: View > Command Palette > Remote-Containers: Open Folder in Container
185 | - Wait for container to build -- expected to take several minutes
186 | - Open a new terminal
187 | 3. Subsequent times: Click **Reopen in Container** and wait for container to spin up
188 | 
189 | 1. Continue on step 7 above
190 |
191 |
192 | #### bells and whistles
193 |
194 | There's some bells and whistles defined in the [.devcontainer.json]().devcontainer.json) that are worth calling out. Also a great reference is the [Setting up VSCode for dbt](https://dbt-msft.github.io/dbt-msft-docs/docs/guides/vscode_setup/) guide.
195 |
196 | 1. there is syntax highlighting provided by the `vdcode-dbt` extension. However, it is configured such that files in your `target/run` and `target/compiled` folder are not syntax highlighted, as a reminder that these files are not where you should be making changes!
197 | 2. basic `sqlfluff` linting is enabled as you type. Syntax errors will be underlined in red at the error, and will also be surfaced in the **Problems** tab of the Terminal pane. It's configured to lint as you type.
198 | 3. Autocompletion is enabled for generic dbt macros via the `vdcode-dbt` extension. For example, if you type `macro` you'll notice a pop up that you can select with the arrow keys then click tab to get a macro snippet.
199 | 
200 | 
201 | 4. the `find-related` extension allows an easy shortcut to navigating using `CMD`+`R`to jump from
202 | - a model file to it's corresponding compiled version,
203 | - from a compiled file to either the original model file or the version in `target/run`
204 | 5. The `vscode-yaml` YAML, combined with the JSON schema defined in [dbt-labs/dbt-jsonschema](https://github.com/dbt-labs/dbt-jsonschema), autocomplete options while working with dbt's YAML files: i.e. :
205 | - Project definition files (`dbt_project.yml`)
206 | - Package files (`packages.yml`)
207 | - Selectors files (`selectors.yml`)
208 | - Property files (`models/whatever.yml`)
209 |
210 |
211 |
212 |
213 |
214 | ### Step-by-step explanation
215 |
216 | To get up and running with this project:
217 |
218 | 1. Clone this repository.
219 |
220 | 1. Change into the `jaffle_shop_duck` directory from the command line:
221 | ```shell
222 | cd jaffle_shop_duckdb
223 | ```
224 |
225 | 1. Install dbt and DuckDB in a virtual environment.
226 |
227 | Expand your shell below:
228 |
229 |
230 | POSIX bash/zsh
231 |
232 | ```shell
233 | python3 -m venv venv
234 | source venv/bin/activate
235 | python3 -m pip install --upgrade pip
236 | python3 -m pip install -r requirements.txt
237 | source venv/bin/activate
238 | ```
239 |
240 |
241 |
242 | POSIX fish
243 |
244 | ```shell
245 | python3 -m venv venv
246 | source venv/bin/activate.fish
247 | python3 -m pip install --upgrade pip
248 | python3 -m pip install -r requirements.txt
249 | source venv/bin/activate.fish
250 | ```
251 |
252 |
253 |
254 | POSIX csh/tcsh
255 |
256 | ```shell
257 | python3 -m venv venv
258 | source venv/bin/activate.csh
259 | python3 -m pip install --upgrade pip
260 | python3 -m pip install -r requirements.txt
261 | source venv/bin/activate.csh
262 | ```
263 |
264 |
265 |
266 | POSIX PowerShell Core
267 |
268 | ```shell
269 | python3 -m venv venv
270 | venv/bin/Activate.ps1
271 | python3 -m pip install --upgrade pip
272 | python3 -m pip install -r requirements.txt
273 | venv/bin/Activate.ps1
274 | ```
275 |
276 |
277 |
278 | Windows cmd.exe
279 |
280 | ```shell
281 | python -m venv venv
282 | venv\Scripts\activate.bat
283 | python -m pip install --upgrade pip
284 | python -m pip install -r requirements.txt
285 | venv\Scripts\activate.bat
286 | ```
287 |
288 |
289 |
290 | Windows PowerShell
291 |
292 | ```shell
293 | python -m venv venv
294 | venv\Scripts\Activate.ps1
295 | python -m pip install --upgrade pip
296 | python -m pip install -r requirements.txt
297 | venv\Scripts\Activate.ps1
298 | ```
299 |
300 |
301 |
302 |
303 | *Why a 2nd activation of the virtual environment?*
304 |
305 | This may not be necessary for many users, but might be for some. Read on for a first-person report from @dbeatty10.
306 |
307 | I use `zsh` as my shell on my MacBook Pro, and I use `pyenv` to manage my Python environments. I already had an alpha version of dbt Core 1.2 installed (and yet another via [pipx](https://pypa.github.io/pipx/installation/)):
308 | ```shell
309 | $ which dbt
310 | /Users/dbeatty/.pyenv/shims/dbt
311 | ```
312 | ```shell
313 | $ dbt --version
314 | Core:
315 | - installed: 1.2.0-a1
316 | - latest: 1.1.1 - Ahead of latest version!
317 |
318 | Plugins:
319 | - bigquery: 1.2.0a1 - Ahead of latest version!
320 | - snowflake: 1.2.0a1 - Ahead of latest version!
321 | - redshift: 1.2.0a1 - Ahead of latest version!
322 | - postgres: 1.2.0a1 - Ahead of latest version!
323 | ```
324 |
325 | Then I ran all the steps to create a virtual environment and install the requirements of our DuckDB-based Jaffle Shop repo:
326 | ```shell
327 | $ python3 -m venv venv
328 | $ source venv/bin/activate
329 | (venv) $ python3 -m pip install --upgrade pip
330 | (venv) $ python3 -m pip install -r requirements.txt
331 | ```
332 |
333 | Let's examine where `dbt` is installed and which version it is reporting:
334 | ```shell
335 | (venv) $ which dbt
336 | /Users/dbeatty/projects/jaffle_duck/venv/bin/dbt
337 | ```
338 |
339 | ```shell
340 | (venv) $ dbt --version
341 | Core:
342 | - installed: 1.2.0-a1
343 | - latest: 1.1.1 - Ahead of latest version!
344 |
345 | Plugins:
346 | - bigquery: 1.2.0a1 - Ahead of latest version!
347 | - snowflake: 1.2.0a1 - Ahead of latest version!
348 | - redshift: 1.2.0a1 - Ahead of latest version!
349 | - postgres: 1.2.0a1 - Ahead of latest version!
350 | ```
351 |
352 | ❌ That isn't what we expected -- something isn't right. 😢
353 |
354 | So let's reactivate the virtual environment and try again...
355 | ```shell
356 | (venv) $ source venv/bin/activate
357 | ```
358 |
359 | ```shell
360 | (venv) $ dbt --version
361 | Core:
362 | - installed: 1.1.1
363 | - latest: 1.1.1 - Up to date!
364 |
365 | Plugins:
366 | - postgres: 1.1.1 - Up to date!
367 | - duckdb: 1.1.3 - Up to date!
368 | ```
369 |
370 | ✅ This is what we want -- the 2nd reactivation worked. 😎
371 |
372 |
373 | 1. Ensure your [profile](https://docs.getdbt.com/reference/profiles.yml) is setup correctly from the command line:
374 | ```shell
375 | dbt --version
376 | dbt debug
377 | ```
378 |
379 | 1. Load the CSVs with the demo data set, run the models, and test the output of the models using the [dbt build](https://docs.getdbt.com/reference/commands/build) command:
380 | ```shell
381 | dbt build
382 | ```
383 |
384 | 1. Query the data:
385 |
386 | Launch a DuckDB command-line interface (CLI):
387 | ```shell
388 | duckcli jaffle_shop.duckdb
389 | ```
390 |
391 | Run a query at the prompt and exit:
392 | ```
393 | select * from customers where customer_id = 42;
394 | exit;
395 | ```
396 |
397 | Alternatively, use a single-liner to perform the query:
398 | ```shell
399 | duckcli jaffle_shop.duckdb -e "select * from customers where customer_id = 42"
400 | ```
401 | or:
402 | ```shell
403 | echo 'select * from customers where customer_id = 42' | duckcli jaffle_shop.duckdb
404 | ```
405 |
406 | 1. Generate and view the documentation for the project:
407 | ```shell
408 | dbt docs generate
409 | dbt docs serve
410 | ```
411 |
412 | ## Running `build` steps independently
413 |
414 | 1. Load the CSVs with the demo data set. This materializes the CSVs as tables in your target schema. Note that a typical dbt project **does not require this step** since dbt assumes your raw data is already in your warehouse.
415 | ```shell
416 | dbt seed
417 | ```
418 |
419 | 1. Run the models:
420 | ```shell
421 | dbt run
422 | ```
423 |
424 | > **NOTE:** If you decide to run this project in your own data warehouse (outside of this DuckDB demo) and steps fail, it might mean that you need to make small changes to the SQL in the models folder to adjust for the flavor of SQL of your target database. Definitely consider this if you are using a community-contributed adapter.
425 |
426 | 1. Test the output of the models using the [test](https://docs.getdbt.com/reference/commands/test) command:
427 | ```shell
428 | dbt test
429 | ```
430 |
431 | ## Browsing the data
432 | Some options:
433 | - [DuckDB UI](https://duckdb.org/docs/stable/extensions/ui.html)
434 | - [duckcli](https://pypi.org/project/duckcli/)
435 | - [DuckDB CLI](https://duckdb.org/docs/installation/?environment=cli)
436 | - [How to set up DBeaver SQL IDE for DuckDB](https://duckdb.org/docs/guides/sql_editors/dbeaver)
437 |
438 | ### Troubleshooting
439 |
440 | You may get an error like this, in which case you will need to disconnect from any sessions that are locking the database:
441 | ```
442 | IO Error: Could not set lock on file "jaffle_shop.duckdb": Resource temporarily unavailable
443 | ```
444 |
445 | This is a known issue in DuckDB. If you are using DBeaver, this means shutting down DBeaver (merely disconnecting didn't work for me).
446 |
447 | Very worst-case, deleting the database file will get you back in action (BUT you will lose all your data).
448 |
449 |
450 | #### GitHub Codespaces and VSCode Remote Container
451 |
452 | If you're using a privacy-forward browser such as Firefox and Brave, or a tracking-cookie-blocking extension like UBlock Origin or Privacy Badger, you may see the below error. You can either change your cookie settings, use a browser like Chrome, or just ignore the error because it doesn't affect the demo
453 |
454 | 
455 |
456 | ---
457 | For more information on dbt:
458 | - Read the [introduction to dbt](https://docs.getdbt.com/docs/introduction)
459 | - Read the [dbt viewpoint](https://docs.getdbt.com/docs/about/viewpoint)
460 | - Join the [dbt Community](http://community.getdbt.com/)
461 |
--------------------------------------------------------------------------------
/RELEASE.md:
--------------------------------------------------------------------------------
1 | # Release instructions
2 |
3 | 1. Install `pip-tools`:
4 | ```shell
5 | python -m pip install -r requirements-dev.txt
6 | ```
7 | 1. Run `pip-compile` to pin all the dependencies and update `requirements.txt`:
8 | ```shell
9 | pip-compile
10 | ```
11 | 1. Commit the result.
12 | 1. Open a PR.
13 |
--------------------------------------------------------------------------------
/dbt-completion.bash:
--------------------------------------------------------------------------------
1 | #/usr/bin/env bash
2 |
3 | # OVERVIEW
4 | # Adds autocompletion to dbt CLI by:
5 | # 1. Finding the root of the repo (identified by dbt_project.yml
6 | # 2. Parsing target/manifest.json file, extracting valid model selectors
7 | # 3. Doing some bash magic to autocomplete selectors for:
8 | # -m
9 | # --model[s]
10 | # -s
11 | # --select
12 | # --exclude
13 | #
14 | # NOTE: This script uses the manifest (assumed to be at target/manifest.json)
15 | # to _quickly_ provide a list of existing selectors. As such, a dbt
16 | # resource must be compiled before it will be available for tab completion.
17 | # In the future, this script should use dbt directly to parse the project
18 | # directory and generate possible selectors. Until then, brand new
19 | # models/sources/tags/packages will not be displayed in the tab complete menu
20 | #
21 | # INSTALLATION
22 | # 1. Copy dbt-completion.bash to your home directory (as a dotfile, probably)
23 | # cp dbt-completion.bash ~/.dbt-completion.bash
24 | #
25 | # 2. Source it
26 | # source ~/.dbt-completion.bash
27 | #
28 | # 3. Install it into your ~/.profile file
29 | # echo 'source ~/.dbt-completion.bash' >> ~/.bash_profile
30 | #
31 | # 4. Use it with:
32 | # dbt run --models snow
33 | #
34 | #
35 | # CREDITS
36 | # Made possible by this great tutorial on bash completion:
37 | # https://iridakos.com/tutorials/2018/03/01/bash-programmable-completion-tutorial.html
38 | #
39 | # Inspired by git-completion.bash
40 | # https://github.com/git/git/blob/master/contrib/completion/git-completion.bash
41 |
42 |
43 | # Inline a python script so we can deploy this as a single file
44 | # the idea of doing this in bash natively is... daunting
45 | _parse_manifest() {
46 | manifest_path=$1
47 | prefix=$2
48 | prog=$(cat <
61 |
62 | prefix = sys.argv.pop() if len(sys.argv) == 2 else ""
63 |
64 | manifest = json.loads("\n".join([line for line in fileinput.input()]))
65 |
66 | models = set(
67 | "{}{}".format(prefix, node['name'])
68 | for node in manifest['nodes'].values()
69 | if node['resource_type'] in ['model', 'seed']
70 | )
71 |
72 | tags = set(
73 | "{}tag:{}".format(prefix, tag)
74 | for node in manifest['nodes'].values()
75 | for tag in node.get('tags', [])
76 | if node['resource_type'] == 'model'
77 | )
78 |
79 | # The + prefix for sources is not sensible, but allowed.
80 | # This script shouldn't be opinionated about these things
81 | sources = set(
82 | "{}source:{}".format(prefix, node['source_name'])
83 | for node in manifest['nodes'].values()
84 | if node['resource_type'] == 'source'
85 | ) | set(
86 | "{}source:{}.{}".format(prefix, node['source_name'], node['name'])
87 | for node in manifest['nodes'].values()
88 | if node['resource_type'] == 'source'
89 | )
90 |
91 | # Generate partial Fully Qualified Names with a wildcard
92 | # suffix. This matches things like directories and packag names
93 | fqns = set(
94 | "{}{}.*".format(prefix, ".".join(node['fqn'][:i-1]))
95 | for node in manifest['nodes'].values()
96 | for i in range(len(node.get('fqn', [])))
97 | if node['resource_type'] == 'model'
98 | )
99 |
100 | selectors = [
101 | selector
102 | for selector in (models | tags | sources | fqns)
103 | if selector != ''
104 | ]
105 |
106 | print("\n".join(selectors))
107 | except Exception as e:
108 | print(e)
109 | # oops!
110 | pass
111 | EOF
112 | )
113 |
114 | cat "$manifest_path" | python -c "$prog" $prefix
115 | }
116 |
117 | # Iterate backwards in the arg list from the index
118 | # and return the first flag that we find (ie. an
119 | # argument that begins with a '-'
120 | _get_last_flag() {
121 | arg_index=$1
122 | shift
123 | arg_list=("$@")
124 |
125 | first_flag=""
126 | for i in $(seq $arg_index -1 0); do
127 | arg=${arg_list[$i]}
128 | if [[ $arg == -* ]] ; then
129 | first_flag=$arg
130 | break
131 | fi
132 | done
133 |
134 | echo $first_flag
135 | }
136 |
137 | # Return 0 if the supplied flag accepts a selector as an argument
138 | # or 1 if it does not. Python's argparse supports flag prefixes
139 | # so, this method matches both --model and --models. Probably not
140 | # appropriate to support prefixes of exclude, for instance
141 | _flag_is_selector() {
142 | flag=$1
143 |
144 | if [[ $flag == '-m' ]] || \
145 | [[ $flag == --model* ]] || \
146 | [[ $flag == '-s' ]] || \
147 | [[ $flag == '--select' ]] || \
148 | [[ $flag == '--exclude' ]] ;
149 | then
150 | echo 0
151 | else
152 | echo 1
153 | fi
154 | }
155 |
156 | # Pluck out and return the first character in the arg if it is
157 | # a supported node selection modifier (ie. + or @)
158 | _get_arg_prefix() {
159 | arg=$1
160 | first_char=${arg:0:1}
161 | if [[ $first_char == '+' ]] || [[ $first_char == '@' ]] ; then
162 | echo "$first_char"
163 | else
164 | echo ""
165 | fi
166 | }
167 |
168 | # Walk up the filesystem until we find a dbt_project.yml file,
169 | # then return the path which contains it (if found)
170 | _get_project_root() {
171 | slashes=${PWD//[^\/]/}
172 | directory="$PWD"
173 | for (( n=${#slashes}; n>0; --n ))
174 | do
175 | test -e "$directory/dbt_project.yml" && echo "$directory" && return
176 | directory="$directory/.."
177 | done
178 | }
179 |
180 |
181 | # Core bash completion logic
182 | _complete_it() {
183 | # Requires bash-completion, used to handle ':' chars in args
184 | if [[ -n "$BASH" ]] && [[ $(type -t _get_comp_words_by_ref) == 'function' ]]; then
185 | local cur
186 | _get_comp_words_by_ref -n : cur
187 | fi
188 |
189 | # Find the first present flag to the left of the cursor, then
190 | # determine if the flag operates as a node selector
191 | last_flag=$(_get_last_flag $COMP_CWORD "${COMP_WORDS[@]}")
192 | is_selector=$(_flag_is_selector $last_flag)
193 |
194 | if [[ $is_selector == 0 ]] ; then
195 | current_arg="${COMP_WORDS[$COMP_CWORD]}"
196 | prefix=$(_get_arg_prefix $current_arg)
197 | project_dir=$(_get_project_root)
198 | manifest_path="${project_dir}/target/manifest.json"
199 |
200 | # Bail out if we can't find a manifest
201 | if [ ! -f "$manifest_path" ] ; then
202 | return
203 | fi
204 |
205 | selectors=$(_parse_manifest "$manifest_path" $prefix)
206 |
207 | # If the cursor is in the middle of a flag, don't try to tab complete
208 | # it. This would lead to errors with compgen. Otherwise, supply the
209 | # possible selectors to the compgen program
210 | if [[ $current_arg == -* ]] ; then
211 | COMPREPLY=($(compgen -W "$selectors" ""))
212 | else
213 | COMPREPLY=($(compgen -W "$selectors" "$current_arg"))
214 | fi
215 |
216 | # Requires bash-completion, used to handle ':' chars in args
217 | if [[ -n "$BASH" ]] && [[ $(type -t __ltrim_colon_completions ) == 'function' ]] ; then
218 | __ltrim_colon_completions "$cur"
219 | fi
220 | fi
221 | }
222 |
223 | complete -F _complete_it dbt
224 |
--------------------------------------------------------------------------------
/dbt_project.yml:
--------------------------------------------------------------------------------
1 | name: 'jaffle_shop'
2 |
3 | config-version: 2
4 | version: '0.1'
5 |
6 | profile: 'jaffle_shop'
7 |
8 | model-paths: ["models"]
9 | seed-paths: ["seeds"]
10 | test-paths: ["tests"]
11 | analysis-paths: ["analysis"]
12 | macro-paths: ["macros"]
13 |
14 | target-path: "target"
15 | clean-targets:
16 | - "target"
17 | - "dbt_modules"
18 | - "logs"
19 |
20 | require-dbt-version: [">=1.0.0", "<2.0.0"]
21 |
22 | seeds:
23 | +docs:
24 | node_color: '#cd7f32'
25 |
26 | models:
27 | jaffle_shop:
28 | +materialized: table
29 | staging:
30 | +materialized: view
31 | +docs:
32 | node_color: 'silver'
33 | +docs:
34 | node_color: 'gold'
35 |
--------------------------------------------------------------------------------
/etc/dbdiagram_definition.txt:
--------------------------------------------------------------------------------
1 | Table orders {
2 | id int PK
3 | user_id int
4 | order_date date
5 | status varchar
6 | }
7 |
8 | Table payments {
9 | id int
10 | order_id int
11 | payment_method int
12 | amount int
13 | }
14 |
15 | Table customers {
16 | id int PK
17 | first_name varchar
18 | last_name varchar
19 | }
20 |
21 | Ref: orders.user_id > customers.id
22 |
23 | Ref: payments.order_id > orders.id
24 |
--------------------------------------------------------------------------------
/etc/jaffle_shop_erd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbt-labs/jaffle_shop_duckdb/def2ee2ada92b6409b7ad5031bee9fdda9ec4877/etc/jaffle_shop_erd.png
--------------------------------------------------------------------------------
/images/dbt_full_deploy_commands.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbt-labs/jaffle_shop_duckdb/def2ee2ada92b6409b7ad5031bee9fdda9ec4877/images/dbt_full_deploy_commands.png
--------------------------------------------------------------------------------
/images/dbt_performance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbt-labs/jaffle_shop_duckdb/def2ee2ada92b6409b7ad5031bee9fdda9ec4877/images/dbt_performance.png
--------------------------------------------------------------------------------
/images/open_in_codespaces.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbt-labs/jaffle_shop_duckdb/def2ee2ada92b6409b7ad5031bee9fdda9ec4877/images/open_in_codespaces.png
--------------------------------------------------------------------------------
/models/customers.sql:
--------------------------------------------------------------------------------
1 | with customers as (
2 |
3 | select * from {{ ref('stg_customers') }}
4 |
5 | ),
6 |
7 | orders as (
8 |
9 | select * from {{ ref('stg_orders') }}
10 |
11 | ),
12 |
13 | payments as (
14 |
15 | select * from {{ ref('stg_payments') }}
16 |
17 | ),
18 |
19 | customer_orders as (
20 |
21 | select
22 | customer_id,
23 |
24 | min(order_date) as first_order,
25 | max(order_date) as most_recent_order,
26 | count(order_id) as number_of_orders
27 | from orders
28 |
29 | group by customer_id
30 |
31 | ),
32 |
33 | customer_payments as (
34 |
35 | select
36 | orders.customer_id,
37 | sum(amount) as total_amount
38 |
39 | from payments
40 |
41 | left join orders on
42 | payments.order_id = orders.order_id
43 |
44 | group by orders.customer_id
45 |
46 | ),
47 |
48 | final as (
49 |
50 | select
51 | customers.customer_id,
52 | customers.first_name,
53 | customers.last_name,
54 | customer_orders.first_order,
55 | customer_orders.most_recent_order,
56 | customer_orders.number_of_orders,
57 | customer_payments.total_amount as customer_lifetime_value
58 |
59 | from customers
60 |
61 | left join customer_orders
62 | on customers.customer_id = customer_orders.customer_id
63 |
64 | left join customer_payments
65 | on customers.customer_id = customer_payments.customer_id
66 |
67 | )
68 |
69 | select * from final
70 |
--------------------------------------------------------------------------------
/models/docs.md:
--------------------------------------------------------------------------------
1 | {% docs orders_status %}
2 |
3 | Orders can be one of the following statuses:
4 |
5 | | status | description |
6 | |----------------|------------------------------------------------------------------------------------------------------------------------|
7 | | placed | The order has been placed but has not yet left the warehouse |
8 | | shipped | The order has ben shipped to the customer and is currently in transit |
9 | | completed | The order has been received by the customer |
10 | | return_pending | The customer has indicated that they would like to return the order, but it has not yet been received at the warehouse |
11 | | returned | The order has been returned by the customer and received at the warehouse |
12 |
13 |
14 | {% enddocs %}
15 |
--------------------------------------------------------------------------------
/models/orders.sql:
--------------------------------------------------------------------------------
1 | {% set payment_methods = ['credit_card', 'coupon', 'bank_transfer', 'gift_card'] %}
2 |
3 | with orders as (
4 |
5 | select * from {{ ref('stg_orders') }}
6 |
7 | ),
8 |
9 | payments as (
10 |
11 | select * from {{ ref('stg_payments') }}
12 |
13 | ),
14 |
15 | order_payments as (
16 |
17 | select
18 | order_id,
19 |
20 | {% for payment_method in payment_methods -%}
21 | sum(case when payment_method = '{{ payment_method }}' then amount else 0 end) as {{ payment_method }}_amount,
22 | {% endfor -%}
23 |
24 | sum(amount) as total_amount
25 |
26 | from payments
27 |
28 | group by order_id
29 |
30 | ),
31 |
32 | final as (
33 |
34 | select
35 | orders.order_id,
36 | orders.customer_id,
37 | orders.order_date,
38 | orders.status,
39 |
40 | {% for payment_method in payment_methods -%}
41 |
42 | order_payments.{{ payment_method }}_amount,
43 |
44 | {% endfor -%}
45 |
46 | order_payments.total_amount as amount
47 |
48 | from orders
49 |
50 |
51 | left join order_payments
52 | on orders.order_id = order_payments.order_id
53 |
54 | )
55 |
56 | select * from final
57 |
--------------------------------------------------------------------------------
/models/overview.md:
--------------------------------------------------------------------------------
1 | {% docs __overview__ %}
2 |
3 | ## Data Documentation for Jaffle Shop
4 |
5 | `jaffle_shop` is a fictional ecommerce store.
6 |
7 | This [dbt](https://www.getdbt.com/) project is for testing out code.
8 |
9 | The source code can be found [here](https://github.com/clrcrl/jaffle_shop).
10 |
11 | {% enddocs %}
12 |
--------------------------------------------------------------------------------
/models/schema.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | models:
4 | - name: customers
5 | description: This table has basic information about a customer, as well as some derived facts based on a customer's orders
6 |
7 | columns:
8 | - name: customer_id
9 | description: This is a unique identifier for a customer
10 | tests:
11 | - unique
12 | - not_null
13 |
14 | - name: first_name
15 | description: Customer's first name. PII.
16 |
17 | - name: last_name
18 | description: Customer's last name. PII.
19 |
20 | - name: first_order
21 | description: Date (UTC) of a customer's first order
22 |
23 | - name: most_recent_order
24 | description: Date (UTC) of a customer's most recent order
25 |
26 | - name: number_of_orders
27 | description: Count of the number of orders a customer has placed
28 |
29 | - name: total_order_amount
30 | description: Total value (AUD) of a customer's orders
31 |
32 | - name: orders
33 | description: This table has basic information about orders, as well as some derived facts based on payments
34 |
35 | columns:
36 | - name: order_id
37 | tests:
38 | - unique
39 | - not_null
40 | description: This is a unique identifier for an order
41 |
42 | - name: customer_id
43 | description: Foreign key to the customers table
44 | tests:
45 | - not_null
46 | - relationships:
47 | to: ref('customers')
48 | field: customer_id
49 |
50 | - name: order_date
51 | description: Date (UTC) that the order was placed
52 |
53 | - name: status
54 | description: '{{ doc("orders_status") }}'
55 | tests:
56 | - accepted_values:
57 | values: ['placed', 'shipped', 'completed', 'return_pending', 'returned']
58 |
59 | - name: amount
60 | description: Total amount (AUD) of the order
61 | tests:
62 | - not_null
63 |
64 | - name: credit_card_amount
65 | description: Amount of the order (AUD) paid for by credit card
66 | tests:
67 | - not_null
68 |
69 | - name: coupon_amount
70 | description: Amount of the order (AUD) paid for by coupon
71 | tests:
72 | - not_null
73 |
74 | - name: bank_transfer_amount
75 | description: Amount of the order (AUD) paid for by bank transfer
76 | tests:
77 | - not_null
78 |
79 | - name: gift_card_amount
80 | description: Amount of the order (AUD) paid for by gift card
81 | tests:
82 | - not_null
83 |
--------------------------------------------------------------------------------
/models/staging/schema.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | models:
4 | - name: stg_customers
5 | columns:
6 | - name: customer_id
7 | tests:
8 | - unique
9 | - not_null
10 |
11 | - name: stg_orders
12 | columns:
13 | - name: order_id
14 | tests:
15 | - unique
16 | - not_null
17 | - name: status
18 | tests:
19 | - accepted_values:
20 | values: ['placed', 'shipped', 'completed', 'return_pending', 'returned']
21 |
22 | - name: stg_payments
23 | columns:
24 | - name: payment_id
25 | tests:
26 | - unique
27 | - not_null
28 | - name: payment_method
29 | tests:
30 | - accepted_values:
31 | values: ['credit_card', 'coupon', 'bank_transfer', 'gift_card']
32 |
--------------------------------------------------------------------------------
/models/staging/stg_customers.sql:
--------------------------------------------------------------------------------
1 | with source as (
2 |
3 | {#-
4 | Normally we would select from the table here, but we are using seeds to load
5 | our data in this project
6 | #}
7 | select * from {{ ref('raw_customers') }}
8 |
9 | ),
10 |
11 | renamed as (
12 |
13 | select
14 | id as customer_id,
15 | first_name,
16 | last_name
17 |
18 | from source
19 |
20 | )
21 |
22 | select * from renamed
23 |
--------------------------------------------------------------------------------
/models/staging/stg_orders.sql:
--------------------------------------------------------------------------------
1 | with source as (
2 |
3 | {#-
4 | Normally we would select from the table here, but we are using seeds to load
5 | our data in this project
6 | #}
7 | select * from {{ ref('raw_orders') }}
8 |
9 | ),
10 |
11 | renamed as (
12 |
13 | select
14 | id as order_id,
15 | user_id as customer_id,
16 | order_date,
17 | status
18 |
19 | from source
20 |
21 | )
22 |
23 | select * from renamed
24 |
--------------------------------------------------------------------------------
/models/staging/stg_payments.sql:
--------------------------------------------------------------------------------
1 | with source as (
2 |
3 | {#-
4 | Normally we would select from the table here, but we are using seeds to load
5 | our data in this project
6 | #}
7 | select * from {{ ref('raw_payments') }}
8 |
9 | ),
10 |
11 | renamed as (
12 |
13 | select
14 | id as payment_id,
15 | order_id,
16 | payment_method,
17 |
18 | -- `amount` is currently stored in cents, so we convert it to dollars
19 | amount / 100 as amount
20 |
21 | from source
22 |
23 | )
24 |
25 | select * from renamed
26 |
--------------------------------------------------------------------------------
/profiles.yml:
--------------------------------------------------------------------------------
1 | jaffle_shop:
2 |
3 | target: dev
4 | outputs:
5 | dev:
6 | type: duckdb
7 | path: 'jaffle_shop.duckdb'
8 | threads: 24
9 |
--------------------------------------------------------------------------------
/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | pip-tools
2 |
--------------------------------------------------------------------------------
/requirements.in:
--------------------------------------------------------------------------------
1 | # 3rd party CLI for DuckDB
2 | duckcli>=0.2.1
3 |
4 | # Database adapter
5 | dbt-duckdb>=1.9.0,<2.0.0
6 |
7 | # dbt Core 1.9
8 | dbt-core>=1.9.0,<1.10.0
9 |
10 | # extra features
11 | sqlfluff>=2.3.5,<3
12 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | #
2 | # This file is autogenerated by pip-compile with Python 3.12
3 | # by the following command:
4 | #
5 | # pip-compile
6 | #
7 | agate==1.7.1
8 | # via
9 | # dbt-adapters
10 | # dbt-common
11 | # dbt-core
12 | annotated-types==0.6.0
13 | # via pydantic
14 | appdirs==1.4.4
15 | # via sqlfluff
16 | attrs==23.2.0
17 | # via
18 | # jsonschema
19 | # referencing
20 | babel==2.14.0
21 | # via agate
22 | certifi==2024.2.2
23 | # via requests
24 | chardet==5.2.0
25 | # via
26 | # diff-cover
27 | # sqlfluff
28 | charset-normalizer==3.3.2
29 | # via requests
30 | cli-helpers[styles]==2.4.0
31 | # via duckcli
32 | click==8.1.7
33 | # via
34 | # dbt-core
35 | # dbt-semantic-interfaces
36 | # duckcli
37 | # sqlfluff
38 | colorama==0.4.6
39 | # via
40 | # dbt-common
41 | # sqlfluff
42 | configobj==5.0.9
43 | # via
44 | # cli-helpers
45 | # duckcli
46 | daff==1.3.46
47 | # via dbt-core
48 | dbt-adapters==1.14.5
49 | # via
50 | # dbt-core
51 | # dbt-duckdb
52 | dbt-common==1.18
53 | # via
54 | # dbt-adapters
55 | # dbt-core
56 | # dbt-duckdb
57 | dbt-core==1.9.4
58 | # via
59 | # -r requirements.in
60 | # dbt-duckdb
61 | dbt-duckdb==1.9.1
62 | # via -r requirements.in
63 | dbt-extractor==0.5.1
64 | # via dbt-core
65 | dbt-semantic-interfaces==0.7.4
66 | # via dbt-core
67 | deepdiff==7.0.1
68 | # via dbt-common
69 | diff-cover==8.0.3
70 | # via sqlfluff
71 | duckcli==0.2.1
72 | # via -r requirements.in
73 | duckdb==1.2.0
74 | # via
75 | # dbt-duckdb
76 | # duckcli
77 | idna==3.6
78 | # via requests
79 | importlib-metadata==6.11.0
80 | # via dbt-semantic-interfaces
81 | iniconfig==2.0.0
82 | # via pytest
83 | isodate==0.6.1
84 | # via
85 | # agate
86 | # dbt-common
87 | jinja2==3.1.3
88 | # via
89 | # dbt-common
90 | # dbt-core
91 | # dbt-semantic-interfaces
92 | # diff-cover
93 | # sqlfluff
94 | jsonschema==4.21.1
95 | # via
96 | # dbt-common
97 | # dbt-semantic-interfaces
98 | jsonschema-specifications==2023.12.1
99 | # via jsonschema
100 | leather==0.4.0
101 | # via agate
102 | markupsafe==2.1.5
103 | # via jinja2
104 | mashumaro[msgpack]==3.12
105 | # via
106 | # dbt-adapters
107 | # dbt-common
108 | # dbt-core
109 | more-itertools==10.2.0
110 | # via dbt-semantic-interfaces
111 | msgpack==1.0.8
112 | # via mashumaro
113 | networkx==3.1
114 | # via dbt-core
115 | ordered-set==4.1.0
116 | # via deepdiff
117 | packaging==24.0
118 | # via
119 | # dbt-core
120 | # pytest
121 | parsedatetime==2.6
122 | # via agate
123 | pathspec==0.11.2
124 | # via
125 | # dbt-common
126 | # dbt-core
127 | # sqlfluff
128 | pluggy==1.4.0
129 | # via
130 | # diff-cover
131 | # pytest
132 | prompt-toolkit==3.0.50
133 | # via duckcli
134 | protobuf==5.29.4
135 | # via
136 | # dbt-adapters
137 | # dbt-common
138 | # dbt-core
139 | pydantic==2.6.3
140 | # via dbt-semantic-interfaces
141 | pydantic-core==2.16.3
142 | # via pydantic
143 | pygments==2.17.2
144 | # via
145 | # cli-helpers
146 | # diff-cover
147 | # duckcli
148 | pytest==8.1.1
149 | # via sqlfluff
150 | python-dateutil==2.9.0.post0
151 | # via
152 | # dbt-common
153 | # dbt-semantic-interfaces
154 | python-slugify==8.0.4
155 | # via agate
156 | pytimeparse==1.1.8
157 | # via agate
158 | pytz==2024.1
159 | # via
160 | # dbt-adapters
161 | # dbt-core
162 | pyyaml==6.0.1
163 | # via
164 | # dbt-core
165 | # dbt-semantic-interfaces
166 | # sqlfluff
167 | referencing==0.33.0
168 | # via
169 | # jsonschema
170 | # jsonschema-specifications
171 | regex==2023.12.25
172 | # via sqlfluff
173 | requests==2.31.0
174 | # via
175 | # dbt-common
176 | # dbt-core
177 | # snowplow-tracker
178 | rpds-py==0.18.0
179 | # via
180 | # jsonschema
181 | # referencing
182 | six==1.16.0
183 | # via
184 | # isodate
185 | # python-dateutil
186 | snowplow-tracker==1.1.0
187 | # via dbt-core
188 | sqlfluff==2.3.5
189 | # via -r requirements.in
190 | sqlparse==0.5.0
191 | # via
192 | # dbt-core
193 | # duckcli
194 | tabulate[widechars]==0.9.0
195 | # via cli-helpers
196 | tblib==3.0.0
197 | # via sqlfluff
198 | text-unidecode==1.3
199 | # via python-slugify
200 | tqdm==4.66.2
201 | # via sqlfluff
202 | typing-extensions==4.10.0
203 | # via
204 | # dbt-adapters
205 | # dbt-common
206 | # dbt-core
207 | # dbt-semantic-interfaces
208 | # mashumaro
209 | # pydantic
210 | # pydantic-core
211 | # snowplow-tracker
212 | # sqlfluff
213 | urllib3==1.26.18
214 | # via requests
215 | wcwidth==0.2.13
216 | # via
217 | # prompt-toolkit
218 | # tabulate
219 | zipp==3.17.0
220 | # via importlib-metadata
221 |
--------------------------------------------------------------------------------
/seeds/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbt-labs/jaffle_shop_duckdb/def2ee2ada92b6409b7ad5031bee9fdda9ec4877/seeds/.gitkeep
--------------------------------------------------------------------------------
/seeds/raw_customers.csv:
--------------------------------------------------------------------------------
1 | id,first_name,last_name
2 | 1,Michael,P.
3 | 2,Shawn,M.
4 | 3,Kathleen,P.
5 | 4,Jimmy,C.
6 | 5,Katherine,R.
7 | 6,Sarah,R.
8 | 7,Martin,M.
9 | 8,Frank,R.
10 | 9,Jennifer,F.
11 | 10,Henry,W.
12 | 11,Fred,S.
13 | 12,Amy,D.
14 | 13,Kathleen,M.
15 | 14,Steve,F.
16 | 15,Teresa,H.
17 | 16,Amanda,H.
18 | 17,Kimberly,R.
19 | 18,Johnny,K.
20 | 19,Virginia,F.
21 | 20,Anna,A.
22 | 21,Willie,H.
23 | 22,Sean,H.
24 | 23,Mildred,A.
25 | 24,David,G.
26 | 25,Victor,H.
27 | 26,Aaron,R.
28 | 27,Benjamin,B.
29 | 28,Lisa,W.
30 | 29,Benjamin,K.
31 | 30,Christina,W.
32 | 31,Jane,G.
33 | 32,Thomas,O.
34 | 33,Katherine,M.
35 | 34,Jennifer,S.
36 | 35,Sara,T.
37 | 36,Harold,O.
38 | 37,Shirley,J.
39 | 38,Dennis,J.
40 | 39,Louise,W.
41 | 40,Maria,A.
42 | 41,Gloria,C.
43 | 42,Diana,S.
44 | 43,Kelly,N.
45 | 44,Jane,R.
46 | 45,Scott,B.
47 | 46,Norma,C.
48 | 47,Marie,P.
49 | 48,Lillian,C.
50 | 49,Judy,N.
51 | 50,Billy,L.
52 | 51,Howard,R.
53 | 52,Laura,F.
54 | 53,Anne,B.
55 | 54,Rose,M.
56 | 55,Nicholas,R.
57 | 56,Joshua,K.
58 | 57,Paul,W.
59 | 58,Kathryn,K.
60 | 59,Adam,A.
61 | 60,Norma,W.
62 | 61,Timothy,R.
63 | 62,Elizabeth,P.
64 | 63,Edward,G.
65 | 64,David,C.
66 | 65,Brenda,W.
67 | 66,Adam,W.
68 | 67,Michael,H.
69 | 68,Jesse,E.
70 | 69,Janet,P.
71 | 70,Helen,F.
72 | 71,Gerald,C.
73 | 72,Kathryn,O.
74 | 73,Alan,B.
75 | 74,Harry,A.
76 | 75,Andrea,H.
77 | 76,Barbara,W.
78 | 77,Anne,W.
79 | 78,Harry,H.
80 | 79,Jack,R.
81 | 80,Phillip,H.
82 | 81,Shirley,H.
83 | 82,Arthur,D.
84 | 83,Virginia,R.
85 | 84,Christina,R.
86 | 85,Theresa,M.
87 | 86,Jason,C.
88 | 87,Phillip,B.
89 | 88,Adam,T.
90 | 89,Margaret,J.
91 | 90,Paul,P.
92 | 91,Todd,W.
93 | 92,Willie,O.
94 | 93,Frances,R.
95 | 94,Gregory,H.
96 | 95,Lisa,P.
97 | 96,Jacqueline,A.
98 | 97,Shirley,D.
99 | 98,Nicole,M.
100 | 99,Mary,G.
101 | 100,Jean,M.
102 |
--------------------------------------------------------------------------------
/seeds/raw_orders.csv:
--------------------------------------------------------------------------------
1 | id,user_id,order_date,status
2 | 1,1,2018-01-01,returned
3 | 2,3,2018-01-02,completed
4 | 3,94,2018-01-04,completed
5 | 4,50,2018-01-05,completed
6 | 5,64,2018-01-05,completed
7 | 6,54,2018-01-07,completed
8 | 7,88,2018-01-09,completed
9 | 8,2,2018-01-11,returned
10 | 9,53,2018-01-12,completed
11 | 10,7,2018-01-14,completed
12 | 11,99,2018-01-14,completed
13 | 12,59,2018-01-15,completed
14 | 13,84,2018-01-17,completed
15 | 14,40,2018-01-17,returned
16 | 15,25,2018-01-17,completed
17 | 16,39,2018-01-18,completed
18 | 17,71,2018-01-18,completed
19 | 18,64,2018-01-20,returned
20 | 19,54,2018-01-22,completed
21 | 20,20,2018-01-23,completed
22 | 21,71,2018-01-23,completed
23 | 22,86,2018-01-24,completed
24 | 23,22,2018-01-26,return_pending
25 | 24,3,2018-01-27,completed
26 | 25,51,2018-01-28,completed
27 | 26,32,2018-01-28,completed
28 | 27,94,2018-01-29,completed
29 | 28,8,2018-01-29,completed
30 | 29,57,2018-01-31,completed
31 | 30,69,2018-02-02,completed
32 | 31,16,2018-02-02,completed
33 | 32,28,2018-02-04,completed
34 | 33,42,2018-02-04,completed
35 | 34,38,2018-02-06,completed
36 | 35,80,2018-02-08,completed
37 | 36,85,2018-02-10,completed
38 | 37,1,2018-02-10,completed
39 | 38,51,2018-02-10,completed
40 | 39,26,2018-02-11,completed
41 | 40,33,2018-02-13,completed
42 | 41,99,2018-02-14,completed
43 | 42,92,2018-02-16,completed
44 | 43,31,2018-02-17,completed
45 | 44,66,2018-02-17,completed
46 | 45,22,2018-02-17,completed
47 | 46,6,2018-02-19,completed
48 | 47,50,2018-02-20,completed
49 | 48,27,2018-02-21,completed
50 | 49,35,2018-02-21,completed
51 | 50,51,2018-02-23,completed
52 | 51,71,2018-02-24,completed
53 | 52,54,2018-02-25,return_pending
54 | 53,34,2018-02-26,completed
55 | 54,54,2018-02-26,completed
56 | 55,18,2018-02-27,completed
57 | 56,79,2018-02-28,completed
58 | 57,93,2018-03-01,completed
59 | 58,22,2018-03-01,completed
60 | 59,30,2018-03-02,completed
61 | 60,12,2018-03-03,completed
62 | 61,63,2018-03-03,completed
63 | 62,57,2018-03-05,completed
64 | 63,70,2018-03-06,completed
65 | 64,13,2018-03-07,completed
66 | 65,26,2018-03-08,completed
67 | 66,36,2018-03-10,completed
68 | 67,79,2018-03-11,completed
69 | 68,53,2018-03-11,completed
70 | 69,3,2018-03-11,completed
71 | 70,8,2018-03-12,completed
72 | 71,42,2018-03-12,shipped
73 | 72,30,2018-03-14,shipped
74 | 73,19,2018-03-16,completed
75 | 74,9,2018-03-17,shipped
76 | 75,69,2018-03-18,completed
77 | 76,25,2018-03-20,completed
78 | 77,35,2018-03-21,shipped
79 | 78,90,2018-03-23,shipped
80 | 79,52,2018-03-23,shipped
81 | 80,11,2018-03-23,shipped
82 | 81,76,2018-03-23,shipped
83 | 82,46,2018-03-24,shipped
84 | 83,54,2018-03-24,shipped
85 | 84,70,2018-03-26,placed
86 | 85,47,2018-03-26,shipped
87 | 86,68,2018-03-26,placed
88 | 87,46,2018-03-27,placed
89 | 88,91,2018-03-27,shipped
90 | 89,21,2018-03-28,placed
91 | 90,66,2018-03-30,shipped
92 | 91,47,2018-03-31,placed
93 | 92,84,2018-04-02,placed
94 | 93,66,2018-04-03,placed
95 | 94,63,2018-04-03,placed
96 | 95,27,2018-04-04,placed
97 | 96,90,2018-04-06,placed
98 | 97,89,2018-04-07,placed
99 | 98,41,2018-04-07,placed
100 | 99,85,2018-04-09,placed
101 |
--------------------------------------------------------------------------------
/seeds/raw_payments.csv:
--------------------------------------------------------------------------------
1 | id,order_id,payment_method,amount
2 | 1,1,credit_card,1000
3 | 2,2,credit_card,2000
4 | 3,3,coupon,100
5 | 4,4,coupon,2500
6 | 5,5,bank_transfer,1700
7 | 6,6,credit_card,600
8 | 7,7,credit_card,1600
9 | 8,8,credit_card,2300
10 | 9,9,gift_card,2300
11 | 10,9,bank_transfer,0
12 | 11,10,bank_transfer,2600
13 | 12,11,credit_card,2700
14 | 13,12,credit_card,100
15 | 14,13,credit_card,500
16 | 15,13,bank_transfer,1400
17 | 16,14,bank_transfer,300
18 | 17,15,coupon,2200
19 | 18,16,credit_card,1000
20 | 19,17,bank_transfer,200
21 | 20,18,credit_card,500
22 | 21,18,credit_card,800
23 | 22,19,gift_card,600
24 | 23,20,bank_transfer,1500
25 | 24,21,credit_card,1200
26 | 25,22,bank_transfer,800
27 | 26,23,gift_card,2300
28 | 27,24,coupon,2600
29 | 28,25,bank_transfer,2000
30 | 29,25,credit_card,2200
31 | 30,25,coupon,1600
32 | 31,26,credit_card,3000
33 | 32,27,credit_card,2300
34 | 33,28,bank_transfer,1900
35 | 34,29,bank_transfer,1200
36 | 35,30,credit_card,1300
37 | 36,31,credit_card,1200
38 | 37,32,credit_card,300
39 | 38,33,credit_card,2200
40 | 39,34,bank_transfer,1500
41 | 40,35,credit_card,2900
42 | 41,36,bank_transfer,900
43 | 42,37,credit_card,2300
44 | 43,38,credit_card,1500
45 | 44,39,bank_transfer,800
46 | 45,40,credit_card,1400
47 | 46,41,credit_card,1700
48 | 47,42,coupon,1700
49 | 48,43,gift_card,1800
50 | 49,44,gift_card,1100
51 | 50,45,bank_transfer,500
52 | 51,46,bank_transfer,800
53 | 52,47,credit_card,2200
54 | 53,48,bank_transfer,300
55 | 54,49,credit_card,600
56 | 55,49,credit_card,900
57 | 56,50,credit_card,2600
58 | 57,51,credit_card,2900
59 | 58,51,credit_card,100
60 | 59,52,bank_transfer,1500
61 | 60,53,credit_card,300
62 | 61,54,credit_card,1800
63 | 62,54,bank_transfer,1100
64 | 63,55,credit_card,2900
65 | 64,56,credit_card,400
66 | 65,57,bank_transfer,200
67 | 66,58,coupon,1800
68 | 67,58,gift_card,600
69 | 68,59,gift_card,2800
70 | 69,60,credit_card,400
71 | 70,61,bank_transfer,1600
72 | 71,62,gift_card,1400
73 | 72,63,credit_card,2900
74 | 73,64,bank_transfer,2600
75 | 74,65,credit_card,0
76 | 75,66,credit_card,2800
77 | 76,67,bank_transfer,400
78 | 77,67,credit_card,1900
79 | 78,68,credit_card,1600
80 | 79,69,credit_card,1900
81 | 80,70,credit_card,2600
82 | 81,71,credit_card,500
83 | 82,72,credit_card,2900
84 | 83,73,bank_transfer,300
85 | 84,74,credit_card,3000
86 | 85,75,credit_card,1900
87 | 86,76,coupon,200
88 | 87,77,credit_card,0
89 | 88,77,bank_transfer,1900
90 | 89,78,bank_transfer,2600
91 | 90,79,credit_card,1800
92 | 91,79,credit_card,900
93 | 92,80,gift_card,300
94 | 93,81,coupon,200
95 | 94,82,credit_card,800
96 | 95,83,credit_card,100
97 | 96,84,bank_transfer,2500
98 | 97,85,bank_transfer,1700
99 | 98,86,coupon,2300
100 | 99,87,gift_card,3000
101 | 100,87,credit_card,2600
102 | 101,88,credit_card,2900
103 | 102,89,bank_transfer,2200
104 | 103,90,bank_transfer,200
105 | 104,91,credit_card,1900
106 | 105,92,bank_transfer,1500
107 | 106,92,coupon,200
108 | 107,93,gift_card,2600
109 | 108,94,coupon,700
110 | 109,95,coupon,2400
111 | 110,96,gift_card,1700
112 | 111,97,bank_transfer,1400
113 | 112,98,bank_transfer,1000
114 | 113,99,credit_card,2400
115 |
--------------------------------------------------------------------------------