├── LICENSE ├── README.md ├── database └── sample_database.db ├── orm.py ├── pyproject.toml ├── raw_sql.py ├── raw_sql_file.py ├── raw_sql_placeholders.py ├── sql └── top_customers.sql └── sql_query_builer.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 ArjanCodes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raw SQL, SQL Query Builder, or ORM? 2 | 3 | Not sure if you should use Raw SQL, a SQL query builder, or an ORM for your next project? In this video, I’ll explore the pros and cons of each option, allowing you to make an informed decision based on your specific needs. Choosing the wrong technology for your project can be a costly mistake, so it's crucial to understand these tools. 4 | 5 | Video link: https://youtu.be/x1fCJ7sUXCM. 6 | -------------------------------------------------------------------------------- /database/sample_database.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArjanCodes/2023-orm/71793706da1a2ed9217235094c0fc3c6b9d236a5/database/sample_database.db -------------------------------------------------------------------------------- /orm.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pathlib import Path 3 | from typing import Optional 4 | 5 | from sqlalchemy import create_engine, func, select 6 | from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column 7 | from sqlalchemy.types import DateTime, Integer, String 8 | 9 | 10 | class Base(DeclarativeBase): 11 | pass 12 | 13 | 14 | class Customer(Base): 15 | __tablename__ = "Customer" 16 | 17 | id: Mapped[int] = mapped_column(Integer(), primary_key=True, nullable=False) 18 | first_name: Mapped[str] = mapped_column(String(40)) 19 | last_name: Mapped[Optional[str]] = mapped_column(String(20)) 20 | company: Mapped[str] = mapped_column(String(80)) 21 | address: Mapped[str] = mapped_column(String(70)) 22 | city: Mapped[str] = mapped_column(String(40)) 23 | state: Mapped[str] = mapped_column(String(40)) 24 | country: Mapped[str] = mapped_column(String(40)) 25 | postal_code: Mapped[str] = mapped_column(String(10)) 26 | phone: Mapped[str] = mapped_column(String(24)) 27 | fax: Mapped[str] = mapped_column(String(24)) 28 | email: Mapped[str] = mapped_column(String(60), nullable=False) 29 | support_id: Mapped[int] = mapped_column(Integer()) 30 | 31 | def __repr__(self) -> str: 32 | return ( 33 | f"Customer(id={self.id!r}, " 34 | f"first_name={self.first_name!r}, " 35 | f"last_name={self.LastName!r})" 36 | ) 37 | 38 | 39 | class Invoice(Base): 40 | __tablename__ = "Invoice" 41 | 42 | id: Mapped[int] = mapped_column(Integer(), primary_key=True, nullable=False) 43 | customer_id: Mapped[int] = mapped_column(Integer(), nullable=False) 44 | date: Mapped[datetime] = mapped_column(DateTime(), nullable=False) 45 | billing_address: Mapped[str] = mapped_column(String(70)) 46 | billing_city: Mapped[str] = mapped_column(String(40)) 47 | billing_state: Mapped[str] = mapped_column(String(40)) 48 | billing_country: Mapped[str] = mapped_column(String(40)) 49 | billing_postal_code: Mapped[str] = mapped_column(String(10)) 50 | total: Mapped[int] = mapped_column(Integer(), nullable=False) 51 | 52 | def __repr__(self) -> str: 53 | return ( 54 | f"Invoice(id={self.id!r}, " 55 | f"customer_id={self.customer_id!r}, " 56 | f"date={self.date!r})" 57 | ) 58 | 59 | 60 | def main() -> None: 61 | number_of_top_customers = int( 62 | input("How many top customers do you want to query? ") 63 | ) 64 | 65 | db_path = Path("database/sample_database.db").absolute() 66 | 67 | engine = create_engine(rf"sqlite:///{db_path}") 68 | session = Session(engine) 69 | stmt = ( 70 | select( 71 | Customer.id, 72 | Customer.first_name, 73 | func.sum(Invoice.total).label("Total"), 74 | ) 75 | .join(Invoice, Customer.id == Invoice.customer_id) 76 | .group_by(Customer.id, Customer.first_name) 77 | .order_by(func.sum(Invoice.total).label("Total").desc()) 78 | .limit(number_of_top_customers) 79 | ) 80 | 81 | for customer in session.execute(stmt): 82 | print(customer) 83 | 84 | 85 | if __name__ == "__main__": 86 | main() 87 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "orm" 3 | version = "0.1.0" 4 | description = "Code examples for explaining ORM, raw SQL and SQL builder" 5 | authors = ["Author "] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.9" 10 | sqlalchemy = "^2.0.4" 11 | pypika = "^0.48.9" 12 | 13 | 14 | [build-system] 15 | requires = ["poetry-core"] 16 | build-backend = "poetry.core.masonry.api" 17 | -------------------------------------------------------------------------------- /raw_sql.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | 4 | def main() -> None: 5 | number_of_top_customers = int( 6 | input("How many top customers do you want to query? ") 7 | ) 8 | 9 | con = sqlite3.connect("database/sample_database.db") 10 | 11 | cur = con.cursor() 12 | 13 | raw_sql = """ 14 | SELECT 15 | c.id, 16 | c.first_name, 17 | SUM(i.total) AS total 18 | FROM Invoice i 19 | LEFT JOIN Customer c ON i.customer_id = c.id 20 | GROUP BY c.id, c.first_name 21 | ORDER BY total DESC 22 | LIMIT ?; 23 | """ 24 | 25 | for row in cur.execute(raw_sql, (number_of_top_customers,)): 26 | print(row) 27 | 28 | 29 | if __name__ == "__main__": 30 | main() 31 | -------------------------------------------------------------------------------- /raw_sql_file.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from pathlib import Path 3 | 4 | 5 | def read_sql_query(sql_path: str) -> str: 6 | """Read SQL file as string.""" 7 | return Path(sql_path).read_text() 8 | 9 | 10 | def main() -> None: 11 | con = sqlite3.connect("database/sample_database.db") 12 | 13 | number_of_top_customers = int( 14 | input("How many top customers do you want to query? ") 15 | ) 16 | 17 | cur = con.cursor() 18 | 19 | raw_sql = read_sql_query("sql/top_customers.sql") 20 | 21 | placeholder = {"limit": number_of_top_customers} 22 | 23 | for row in cur.execute(raw_sql, placeholder): 24 | print(row) 25 | 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /raw_sql_placeholders.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | 4 | def main() -> None: 5 | number_of_top_customers = int( 6 | input("How many top customers do you want to query? ") 7 | ) 8 | 9 | con = sqlite3.connect("database/sample_database.db") 10 | 11 | cur = con.cursor() 12 | 13 | raw_sql = """ 14 | SELECT 15 | c.id, 16 | c.first_name, 17 | SUM(i.total) AS total 18 | FROM Invoice i 19 | LEFT JOIN Customer c ON i.customer_id = c.id 20 | GROUP BY c.id, c.first_name 21 | ORDER BY total DESC 22 | LIMIT :limit; 23 | """ 24 | 25 | placeholder = { 26 | "limit": number_of_top_customers 27 | } 28 | 29 | for row in cur.execute(raw_sql, placeholder): 30 | print(row) 31 | 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /sql/top_customers.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | c.id, 3 | c.first_name, 4 | SUM(i.total) AS total 5 | FROM Invoice i 6 | LEFT JOIN Customer c ON i.customer_id = c.id 7 | GROUP BY c.id, c.first_name 8 | ORDER BY total DESC 9 | LIMIT :limit; -------------------------------------------------------------------------------- /sql_query_builer.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | from pypika import Order, Query, Table, functions 4 | 5 | 6 | def main() -> None: 7 | number_of_top_customers = int( 8 | input("How many top customers do you want to query? ") 9 | ) 10 | 11 | invoice = Table("Invoice") 12 | customer = Table("Customer") 13 | query = ( 14 | Query.from_(invoice) 15 | .left_join(customer) 16 | .on(invoice.customer_id == customer.id) 17 | .groupby(customer.id, customer.first_name) 18 | .orderby(functions.Sum(invoice.total), order=Order.desc) 19 | .limit(number_of_top_customers) 20 | .select( 21 | customer.id, 22 | customer.first_name, 23 | functions.Sum(invoice.total).as_("total"), 24 | ) 25 | ) 26 | 27 | con = sqlite3.connect("database/sample_database.db") 28 | 29 | cur = con.cursor() 30 | 31 | sql = query.get_sql() 32 | 33 | for row in cur.execute(sql): 34 | print(row) 35 | 36 | 37 | if __name__ == "__main__": 38 | main() 39 | --------------------------------------------------------------------------------