├── README.md ├── northwind.db ├── src ├── index.ts ├── meta.ts └── schema.ts └── tsconfig.json /README.md: -------------------------------------------------------------------------------- 1 | [Drizzle ORM](https://github.com/drizzle-team/drizzle-orm) example with [bun:sqlite](https://bun.sh) module. Blazingly fast ⚡️ 2 | ```shell 3 | bun install 4 | bun run src/index.ts 5 | ``` 6 | 7 | [bunjs docs](https://github.com/oven-sh/bun#Reference), quick installation guide: 8 | ```shell 9 | ## Native: (macOS x64 & Silicon, Linux x64, Windows Subsystem for Linux) 10 | curl -fsSL https://bun.sh/install | bash 11 | 12 | ## Homebrew: (MacOS and Linux) 13 | brew tap oven-sh/bun 14 | brew install bun 15 | 16 | ## upgrade if you have outdated version 17 | bun upgrade 18 | ``` 19 | -------------------------------------------------------------------------------- /northwind.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drizzle-team/drizzle-bun/b9ef08d7faada4d6a6acfc5a657ffc780614c4f9/northwind.db -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { bench, group, run } from "mitata"; 2 | import { Database } from "bun:sqlite"; 3 | import { asc, eq } from "drizzle-orm/expressions"; 4 | import { alias } from "drizzle-orm-sqlite"; 5 | import { SQLiteBunConnector } from "drizzle-orm-sqlite/bun"; 6 | import { sql } from "drizzle-orm"; 7 | import { placeholder } from "drizzle-orm/sql"; 8 | import { 9 | employees, 10 | customers, 11 | suppliers, 12 | products, 13 | orders, 14 | details, 15 | } from "./schema"; 16 | import { 17 | customerIds, 18 | customerSearches, 19 | employeeIds, 20 | orderIds, 21 | productSearches, 22 | } from "./meta"; 23 | 24 | const sqlite = new Database("northwind.db"); 25 | const db = new SQLiteBunConnector(sqlite).connect(); 26 | 27 | const d1 = db.select(customers).prepare(); 28 | const d2 = db 29 | .select(customers) 30 | .where(eq(customers.id, placeholder("userId"))) 31 | .prepare(); 32 | const d3 = db 33 | .select(customers) 34 | .where(sql`${customers.companyName} like ${placeholder("name")}`) 35 | .prepare(); 36 | const d4 = db.select(employees).prepare(); 37 | 38 | const e2 = alias(employees, "recipient"); 39 | const d5 = db 40 | .select(employees) 41 | .leftJoin(e2, eq(e2.id, employees.reportsTo)) 42 | .where(eq(employees.id, placeholder("employeeId"))) 43 | .prepare(); 44 | 45 | const d6 = db.select(suppliers).prepare(); 46 | const d7 = db.select(products).prepare(); 47 | const d8 = db 48 | .select(products) 49 | .where(sql`${products.name} like ${placeholder("name")}`) 50 | .prepare(); 51 | 52 | const d9 = db 53 | .select(orders) 54 | .leftJoin(details, eq(orders.id, details.orderId)) 55 | .leftJoin(products, eq(details.productId, products.id)) 56 | .where(eq(orders.id, placeholder("orderId"))) 57 | .prepare(); 58 | 59 | const d10 = db 60 | .select(orders) 61 | .fields({ 62 | id: orders.id, 63 | shippedDate: orders.shippedDate, 64 | shipName: orders.shipName, 65 | shipCity: orders.shipCity, 66 | shipCountry: orders.shipCountry, 67 | productsCount: sql`count(${details.productId})`.as(), 68 | quantitySum: sql`sum(${details.quantity})`.as(), 69 | totalPrice: 70 | sql`sum(${details.quantity} * ${details.unitPrice})`.as(), 71 | }) 72 | .leftJoin(details, eq(orders.id, details.orderId)) 73 | .groupBy(orders.id) 74 | .orderBy(asc(orders.id)) 75 | .prepare(); 76 | 77 | group({ name: "drizzle", summary: false }, () => { 78 | bench("select * from customer", () => { 79 | d1.execute(); 80 | }); 81 | bench("select * from customer where id = ?", () => { 82 | customerIds.forEach((id) => { 83 | d2.execute({ userId: id }); 84 | }); 85 | }); 86 | 87 | bench("select * from customer where company_name like ?", () => { 88 | customerSearches.forEach((it) => { 89 | d3.execute({ name: `%${it}%` }); 90 | }); 91 | }); 92 | 93 | bench("SELECT * FROM employee", () => { 94 | d4.execute(); 95 | }); 96 | 97 | bench("select * from employee where id = ? left join reportee", () => { 98 | employeeIds.forEach((id) => { 99 | d5.execute({ employeeId: id }); 100 | }); 101 | }); 102 | bench("SELECT * FROM supplier", () => { 103 | d6.execute(); 104 | }); 105 | 106 | bench("SELECT * FROM product", () => { 107 | d7.execute(); 108 | }); 109 | 110 | bench("SELECT * FROM product WHERE product.name LIKE ?", () => { 111 | productSearches.forEach((it) => { 112 | d8.execute({ name: `%${it}%` }); 113 | }); 114 | }); 115 | 116 | bench( 117 | "SELECT * FROM order WHERE order_id = ? LEFT JOIN details and products", 118 | () => { 119 | orderIds.forEach((id) => { 120 | d9.execute({ orderId: id }); 121 | }); 122 | } 123 | ); 124 | 125 | bench("select all order with sum and count", () => { 126 | d10.execute(); 127 | }); 128 | }); 129 | 130 | const main = async () => { 131 | await run(); 132 | process.exit(1); 133 | }; 134 | 135 | main(); 136 | -------------------------------------------------------------------------------- /src/meta.ts: -------------------------------------------------------------------------------- 1 | // prettier-ignore 2 | export const customerIds = [ 3 | 'LAZYK', 'FOLIG', 'QUEDE', 'MORGK', 'SAVEA', 'AROUT', 4 | 'VICTE', 'ISLAT', 'EASTC', 'BOLID', 'SIMOB', 'LEHMS', 5 | 'LETSS', 'FRANS', 'FAMIA', 'LACOR', 'GROSR', 'MEREP', 6 | 'BERGS', 'RICAR', 'CENTC', 'FISSA', 'WANDK', 'BLONP', 7 | 'OCEAN', 'PERIC', 'MAISD', 'LAMAI', 'LINOD', 'BOTTM', 8 | 'NORTS', 'QUEEN', 'LILAS', 'SPLIR', 'WILMK', 'HILAA', 9 | 'LONEP', 'TRAIH', 'SANTG', 'FRANK', 'TRADH', 'WARTH', 10 | 'REGGC', 'RICSU', 'THECR', 'VAFFE', 'ANATR', 'BSBEV', 11 | 'TORTU', 'WOLZA', 'WHITC', 'SUPRD', 'TOMSP', 'HANAR', 12 | 'DRACD', 'RANCH', 'SEVES', 'GODOS', 'CHOPS', 'BONAP', 13 | 'KOENE', 'COMMI', 'CACTU', 'GREAL', 'ALFKI', 'BLAUS', 14 | 'OTTIK', 'WELLI', 'ERNSH', 'OLDWO', 'FRANR', 'PRINI', 15 | 'VINET', 'MAGAA', 'GOURL', 'LAUGB', 'PARIS', 'GALED', 16 | 'DUMON', 'HUNGC', 'QUICK', 'SPECD', 'HUNGO', 'RATTC', 17 | 'PICCO', 'FURIB', 'THEBI', 'ROMEY', 'CONSH', 'FOLKO', 18 | 'ANTON' 19 | ] 20 | 21 | // prettier-ignore 22 | export const customerSearches = [ 23 | "ve", "ey", "or", "bb", "te", 24 | "ab", "ca", "ki", "ap", "be", 25 | "ct", "hi", "er", "pr", "pi", 26 | "en", "au", "ra", "ti", "ke", 27 | "ou", "ur", "me", "ea", "op", 28 | "at", "ne", "na", "os", "ri", 29 | "on", "ha", "il", "to", "as", 30 | "io", "di", "zy", "az", "la", 31 | "ko", "st", "gh", "ug", "ac", 32 | "cc", "ch", "hu", "re", "an", 33 | ]; 34 | 35 | // prettier-ignore 36 | export const productSearches = [ 37 | "ha", "ey", "or", "po", "te", 38 | "ab", "er", "ke", "ap", "be", 39 | "en", "au", "ra", "ti", "su", 40 | "sa", "hi", "nu", "ge", "pi", 41 | "ou", "ur", "me", "ea", "tu", 42 | "at", "ne", "na", "os", "ri", 43 | "on", "ka", "il", "to", "as", 44 | "io", "di", "za", "fa", "la", 45 | "ko", "st", "gh", "ug", "ac", 46 | "cc", "ch", "pa", "re", "an", 47 | ]; 48 | 49 | const employeeIdStart = 1; 50 | const employeeIdEnd = 10; 51 | export const employeeIds = Array.from({ length: employeeIdEnd - employeeIdStart }, (_, i) => i + employeeIdStart); 52 | 53 | const supplierIdStart = 1; 54 | const supplierIdEnd = 30; 55 | export const supplierIds = Array.from({ length: supplierIdEnd - supplierIdStart }, (_, i) => i + supplierIdStart); 56 | 57 | const productIdStart = 1; 58 | const productIdEnd = 78; 59 | export const productIds = Array.from({ length: productIdEnd - productIdStart }, (_, i) => i + productIdEnd); 60 | 61 | const getRandomOrderIds = () => { 62 | const firstId = 10248; 63 | const lastId = 27065; 64 | const orderIds = new Set(); 65 | while (orderIds.size <= 100) orderIds.add(Math.round(firstId + Math.random() * (lastId - firstId))); 66 | return Array.from(orderIds); 67 | }; 68 | 69 | export const orderIds = getRandomOrderIds(); 70 | -------------------------------------------------------------------------------- /src/schema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | InferModel, text, foreignKey, integer, sqliteTable, numeric, 3 | } from "drizzle-orm-sqlite"; 4 | 5 | export const customers = sqliteTable("customer", { 6 | id: text("id").primaryKey(), 7 | companyName: text("company_name").notNull(), 8 | contactName: text("contact_name").notNull(), 9 | contactTitle: text("contact_title").notNull(), 10 | address: text("address").notNull(), 11 | city: text("city").notNull(), 12 | postalCode: text("postal_code"), 13 | region: text("region"), 14 | country: text("country").notNull(), 15 | phone: text("phone").notNull(), 16 | fax: text("fax"), 17 | }); 18 | 19 | export type Customer = InferModel; 20 | 21 | export const employees = sqliteTable("employee", { 22 | id: integer("id").primaryKey(), 23 | lastName: text("last_name").notNull(), 24 | firstName: text("first_name"), 25 | title: text("title").notNull(), 26 | titleOfCourtesy: text("title_of_courtesy").notNull(), 27 | birthDate: integer("birth_date", { mode: "timestamp" }).notNull(), 28 | hireDate: integer("hire_date", { mode: "timestamp" }).notNull(), 29 | address: text("address").notNull(), 30 | city: text("city").notNull(), 31 | postalCode: text("postal_code").notNull(), 32 | country: text("country").notNull(), 33 | homePhone: text("home_phone").notNull(), 34 | extension: integer("extension").notNull(), 35 | notes: text("notes").notNull(), 36 | reportsTo: integer("reports_to"), 37 | photoPath: text("photo_path"), 38 | }, (table) => ({ 39 | reportsToFk: foreignKey(() => ({ 40 | columns: [table.reportsTo], 41 | foreignColumns: [table.id], 42 | })), 43 | })); 44 | 45 | export type Employee = InferModel; 46 | 47 | export const orders = sqliteTable("order", { 48 | id: integer("id").primaryKey(), 49 | orderDate: integer("order_date", { mode: "timestamp" }).notNull(), 50 | requiredDate: integer("required_date", { mode: "timestamp" }).notNull(), 51 | shippedDate: integer("shipped_date", { mode: "timestamp" }), 52 | shipVia: integer("ship_via").notNull(), 53 | freight: numeric("freight").notNull(), 54 | shipName: text("ship_name").notNull(), 55 | shipCity: text("ship_city").notNull(), 56 | shipRegion: text("ship_region"), 57 | shipPostalCode: text("ship_postal_code"), 58 | shipCountry: text("ship_country").notNull(), 59 | customerId: text("customer_id").notNull(), 60 | employeeId: integer("employee_id").notNull(), 61 | }); 62 | 63 | export type Order = InferModel; 64 | 65 | export const suppliers = sqliteTable("supplier", { 66 | id: integer("id").primaryKey({ autoIncrement: true }), 67 | companyName: text("company_name").notNull(), 68 | contactName: text("contact_name").notNull(), 69 | contactTitle: text("contact_title").notNull(), 70 | address: text("address").notNull(), 71 | city: text("city").notNull(), 72 | region: text("region"), 73 | postalCode: text("postal_code").notNull(), 74 | country: text("country").notNull(), 75 | phone: text("phone").notNull(), 76 | }); 77 | 78 | export type Supplier = InferModel; 79 | 80 | export const products = sqliteTable("product", { 81 | id: integer("id").primaryKey({ autoIncrement: true }), 82 | name: text("name").notNull(), 83 | quantityPerUnit: text("quantity_per_unit").notNull(), 84 | unitPrice: numeric("unit_price").notNull(), 85 | unitsInStock: integer("units_in_stock").notNull(), 86 | unitsOnOrder: integer("units_on_order").notNull(), 87 | reorderLevel: integer("reorder_level").notNull(), 88 | discontinued: integer("discontinued").notNull(), 89 | 90 | supplierId: integer("supplier_id").notNull() 91 | .references(() => suppliers.id, { onDelete: "cascade" }), 92 | }); 93 | 94 | export type Product = InferModel; 95 | 96 | export const details = sqliteTable("order_detail", { 97 | unitPrice: numeric("unit_price").notNull(), 98 | quantity: integer("quantity").notNull(), 99 | discount: numeric("discount").notNull(), 100 | orderId: integer("order_id").notNull(), 101 | productId: integer("product_id").notNull(), 102 | }); 103 | 104 | export type Detail = InferModel; 105 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ESNext"], 4 | "module": "esnext", 5 | "target": "esnext", 6 | "moduleResolution": "node", 7 | // "bun-types" is the important part 8 | "types": ["bun-types"] 9 | } 10 | } --------------------------------------------------------------------------------