SQLingvo
Table of Contents
1 DDL
1.1 Copy
Copy from standard input.
(sql (copy :country []
(from :stdin)))
["COPY \"country\" FROM STDIN"]
Copy data from a file into the country table.
(sql (copy :country [:id :name]
(from "/usr1/proj/bray/sql/country_data")))
["COPY \"country\" (\"id\", \"name\") FROM ?" "/usr1/proj/bray/sql/country_data"]
Copy data from a file into the country table with columns in the given order.
(sql (copy :country [:id :name]
(from "/usr1/proj/bray/sql/country_data")))
["COPY \"country\" (\"id\", \"name\") FROM ?" "/usr1/proj/bray/sql/country_data"]
1.2 Create table
Define a new database table.
(sql (create-table :films
(column :code :char :length 5 :primary-key? true)
(column :title :varchar :length 40 :not-null? true)
(column :did :integer :not-null? true)
(column :date-prod :date)
(column :kind :varchar :length 10)
(column :len :interval)
(column :created-at :timestamp-with-time-zone :not-null? true :default '(now))
(column :updated-at :timestamp-with-time-zone :not-null? true :default '(now))))
["CREATE TABLE \"films\" (\"code\" CHAR(5) PRIMARY KEY, \"title\" VARCHAR(40) NOT NULL, \"did\" INTEGER NOT NULL, \"date_prod\" DATE, \"kind\" VARCHAR(10), \"len\" INTERVAL, \"created_at\" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), \"updated_at\" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now())"]
2 SQL
2.1 Delete
Clear the table films.
(sql (delete :films))
["DELETE FROM \"films\""]
Delete all films but musicals.
(sql (delete :films
(where '(<> :kind "Musical"))))
["DELETE FROM \"films\" WHERE (\"kind\" <> ?)" "Musical"]
Delete completed tasks, returning full details of the deleted rows.
(sql (delete :tasks
(where '(= :status "DONE"))
(returning *)))
["DELETE FROM \"tasks\" WHERE (\"status\" = ?) RETURNING *" "DONE"]
2.2 Insert
Insert a single row into table films.
(sql (insert :films []
(values {:code "T_601" :title "Yojimbo" :did 106 :date-prod "1961-06-16" :kind "Drama"})))
["INSERT INTO \"films\" (\"date_prod\", \"title\", \"did\", \"kind\", \"code\") VALUES (?, ?, ?, ?, ?)" "1961-06-16" "Yojimbo" 106 "Drama" "T_601"]
Insert multiple rows into the table films using the multirow VALUES syntax.
(sql (insert :films []
(values [{:code "B6717" :title "Tampopo" :did 110 :date-prod "1985-02-10" :kind "Comedy"},
{:code "HG120" :title "The Dinner Game" :did 140 :date-prod "1985-02-10":kind "Comedy"}])))
["INSERT INTO \"films\" (\"date_prod\", \"title\", \"did\", \"kind\", \"code\") VALUES (?, ?, ?, ?, ?), (?, ?, ?, ?, ?)" "1985-02-10" "Tampopo" 110 "Comedy" "B6717" "1985-02-10" "The Dinner Game" 140 "Comedy" "HG120"]
Insert a row consisting entirely of default values.
(sql (insert :films []
(values :default)))
["INSERT INTO \"films\" DEFAULT VALUES"]
Insert some rows into table films from a table tmpfilms with the same column layout as films.
(sql (insert :films []
(select [*]
(from :tmp-films)
(where '(< :date_prod "2004-05-07")))))
["INSERT INTO \"films\" SELECT * FROM \"tmp_films\" WHERE (\"date_prod\" < ?)" "2004-05-07"]
2.3 Select
Select all films.
(sql (select [*] (from :films)))
["SELECT * FROM \"films\""]
Select all Comedy films.
(sql (select [*]
(from :films)
(where '(= :kind "Comedy"))))
["SELECT * FROM \"films\" WHERE (\"kind\" = ?)" "Comedy"]
Retrieve the most recent weather report for each location.
(sql (select (distinct [:location :time :report] :on [:location])
(from :weather-reports)
(order-by :location (desc :time))))
["SELECT DISTINCT ON (\"location\") \"location\", \"time\", \"report\" FROM \"weather_reports\" ORDER BY \"location\", \"time\" DESC"]
2.4 Update
Change the word Drama to Dramatic in the column kind of the table films.
(sql (update :films {:kind "Dramatic"}
(where '(= :kind "Drama"))))
["UPDATE \"films\" SET \"kind\" = ? WHERE (\"kind\" = ?)" "Dramatic" "Drama"]
2.5 Sorting Rows
The sort expression(s) can be any expression that would be valid in the query's select list.
(sql (select [:a :b]
(from :table-1)
(order-by '(+ :a :b) :c)))
["SELECT \"a\", \"b\" FROM \"table_1\" ORDER BY (\"a\" + \"b\"), \"c\""]
A sort expression can also be the column label
(sql (select [(as '(+ :a :b) :sum) :c]
(from :table-1)
(order-by :sum)))
["SELECT (\"a\" + \"b\") AS \"sum\", \"c\" FROM \"table_1\" ORDER BY \"sum\""]
or the number of an output column.
(sql (select [:a '(max :b)]
(from :table-1)
(group-by :a)
(order-by 1)))
["SELECT \"a\", max(\"b\") FROM \"table_1\" GROUP BY \"a\" ORDER BY 1"]
2.6 With Queries or Common Table Expressions)
(sql (with [:regional-sales
(select [:region (as '(sum :amount) :total-sales)]
(from :orders)
(group-by :region))
:top-regions
(select [:region]
(from :regional-sales)
(where `(> :total-sales
~(select ['(/ (sum :total-sales) 10)]
(from :regional-sales)))))]
(select [:region :product
(as '(sum :quantity) :product-units)
(as '(sum :amount) :product-sales)]
(from :orders)
(where `(in :region ~(select [:region]
(from :top-regions))))
(group-by :region :product))))
["WITH regional_sales AS (SELECT \"region\", sum(\"amount\") AS \"total_sales\" FROM \"orders\" GROUP BY \"region\"), top_regions AS (SELECT \"region\" FROM \"regional_sales\" WHERE (\"total_sales\" > (SELECT (sum(\"total_sales\") / 10) FROM \"regional_sales\"))) SELECT \"region\", \"product\", sum(\"quantity\") AS \"product_units\", sum(\"amount\") AS \"product_sales\" FROM \"orders\" WHERE \"region\" IN (SELECT \"region\" FROM \"top_regions\") GROUP BY \"region\", \"product\""]
3 Database Vendors
Database vendors use different characters to quote identifiers in SQL statements. The `sql` function uses the default PostgreSQL quoting strategy if called with one argument.
(sql (select [:continents.id] (from :continents)))
["SELECT \"continents\".\"id\" FROM \"continents\""]
The quoting strategy can be changed by passing a vendor specification as the first, and the statement as the second argument. The following example uses a quoting strategy for MySQL.
(require '[sqlingvo.vendor :as v]) (sql (v/->mysql) (select [:continents.id] (from :continents)))
["SELECT `continents`.`id` FROM `continents`"]
4 Tips & Tricks
For more complex examples, look at the tests.
5 Emacs
For better indentation in clojure-mode add this to your Emacs config.
(add-hook
'clojure-mode-hook
(lambda ()
(define-clojure-indent
(copy 2)
(create-table 1)
(delete 1)
(drop-table 1)
(insert 2)
(select 1)
(truncate 1)
(update 2))))
6 License
Copyright © 2012-2014 Roman Scherer
Distributed under the Eclipse Public License, the same as Clojure.