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.

Date: 2014-05-28T00:48+0200

Author: Roman Scherer

Org version 7.9.3f with Emacs version 24

Validate XHTML 1.0