本文針對目前最新版9.5.1,若非說明,文中所說文檔即指官方文檔。本人剛接觸PostgreSQL不久,文中難免錯漏,請你們指正;隨着瞭解深刻,本文[可能]會不按期更新補足。php
JSONhtml
PostgreSQL支持Json格式數據,有兩種類型:json和jsonb。二者在效率上有所區別,而這是由於jsonb存儲的是格式化後的二進制數據,因此在寫入時,json類型比較快,而在檢索時(注意這裏說的檢索不是簡單的讀取整個數據,而是好比檢索json數據中某個鍵的值的場景),jsonb效率較高。通常狀況下,使用jsonb就能夠了。json數據是爲了彌補關係型數據在伸縮性擴展性上的不足,可是文檔也說了,不能啥都往裏放,要考慮數據原子性和數據大小。ios
json類型能夠做包含判斷和是否存在的判斷(containment or existence),表示符號分別爲@>和?(以及其它一些變種)。對於這兩種牽涉到多個鍵和元素的判斷場景,json類型比下面要講的arrays更適合,由於其對查詢有內在的優化機制,而array只是單純的線性查找。sql
若json列須要常常檢索,那麼能夠在其上創建GIN索引,jsonb支持兩種特有的GIN索引jsonb_ops和jsonb_path_ops。建立的語法以下:數據庫
CREATE INDEX idxgin ON api USING GIN (jdoc); CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops); -- 只是比前一行多了jsonb_path_ops標記
The jsonb_path_ops supports indexing the @> operator only. 關於這二者使用和技術實現上的區別可參看:PgSQL 9.4 新特性jsonb類型解析,PostgreSQL 9.4 中使用 jsonbexpress
咱們能夠對json數據中的某一屬性建GIN索引(可稱之爲屬性索引),如:CREATE INDEX idxgintags ON api USING GIN ((jdoc -> ’tags’)); 這能提高檢索鍵值對的效率,好比以下場景:json
SELECT jdoc->’guid’, jdoc->’name’ FROM api WHERE jdoc -> ’tags’ ? ’qui’;
固然咱們也能夠不使用屬性索引,而是換一種查詢方式:segmentfault
SELECT jdoc->’guid’, jdoc->’name’ FROM api WHERE jdoc @> ’{"tags": ["qui"]}’;
jsonb also supports btree and hash indexes. These are usually useful only if it’s important to check equality of complete JSON documents.centos
Arrayapi
PostgreSQL支持Array類型,其字段聲明有以下幾種方式:
1 CREATE TABLE emptable ( 2 arraycol1 integer[], 3 arraycol2 text[][], 4 arraycol3 text[3], 5 arraycol4 integer ARRAY, 6 arraycol5 integer ARRAY[4] 7 );
在列聲明時咱們能夠指定數組中的元素類型、維度和長度,後二者然並卵,當前版本的PostgreSQL會忽略這二者的設置,它們更可能是以一種備註的意義存在。
插入格式以下:
INSERT INTO emptable VALUES ( ’{10000, 10000, 10000, 10000}’, ’{{"meeting", "lunch"}, {"training", "presentation"}}’,
ARRAY[10000, 10000, 10000, 10000],
ARRAY[[’meeting’, ’lunch’], [’training’, ’presentation’]] );
注意字符串的寫法,第3行單引號內部是以雙引號包含,第5行ARRAY構造函數方式則是以單引號包含。多維數組中每一個元素的長度要一致,不然會報錯,好比不能
INSERT INTO emptable VALUES ( ’{10000, 10000, 10000, 10000}’, ’{{"meeting", "lunch"}, {"training"}}’ -- error );
訪問,arraycol[n],PostgreSQL的數組默認下標是1基的,這點須要注意,即默認狀況下咱們訪問數組第項應使用arraycol[1],而非慣常的arraycol[0],固然咱們能夠 SET arraycol[-2:7] = '{XXOO,...}'的方式設置數組的上下界(這個例子就變成了-2基);多維數組訪問,以二維數組爲例,arraycol[n][m];若下標超出數組長度則返回null,並不會拋出異常。若訪問數組某部分毗鄰元素,則須要用到slice形式,形如arraycol[1:3][2:5],表示要訪問1到3項,而且取這三項中的2到5項——仍以數組形式——返回,第一個中括號表示第1維,第二個表示第2維,以此類推。須要注意的是arraycol[1:3][2],並非表示取1到3項中的第2項,PostgreSQL認爲只要有一個維度是slice形式,則全部你要訪問的維度都是slice形式,若只有1位數,則前面附加1:,即arraycol[1:3][2] == arraycol[1:3][1:2]。若是slice的下標超出數組長度,又會怎樣呢?有兩種狀況:若起始下標就超出了,那麼返回空數組(文檔中說是由於歷史緣由);若只是結束下標超出,則返回從起始下標到數組末尾這段數據。
一些函數:array_dims,以文本形式返回數組的全部維度;array_length,指定維度的數組長度;array_upper,返回指定維度上界;array_lower,返回指定維度下界;cardinality,全部維度的元素個數總和(不知可否用於子數組或子維度)。
對於一維數組,set arraycol[m] = xxoo,若m大於當前長度,那麼arraycol將自動擴充到m上界,而原上界到新上界之間位置的項將置爲null,重複一遍,目前只有一維數組有這個特性。
array_prepend、array_append、array_cat用於元素的頭尾插入或數組的鏈接,前二者只能用於一維數組,通常咱們可使用鏈接符 || 來提供這三者的功能。
數組檢索相關:any、all、generate_subscripts、array_position、array_positions、&&(左操做數是否包含右操做數)。關於數組檢索,官方文檔有這麼段提示:數組不是集合,搜索數組中的特定元素一般代表你的數據庫設計有問題。 數組字段一般是能夠分裂成獨立的表(with a row for each item of the array)。 很明顯表要容易搜索得多,而且在元素數目很是龐大的時候也能夠更好地伸展。這彷佛表示數組是設計用來進行直接展現的,若業務查詢須要關聯數組中的特定值,則須要考慮從新設計或使用其它類型。
插:在使用MySql的時候,咱們一般會被告知,使用有最大長度的char或者varchar會在性能方面有好處,而在PostgreSQL中,卻不必定是這樣。在PostgreSQL中,這三種類型的字符串數據並無明顯的性能差異,並且character(n)類型的數據通常是最慢的,由於固定長度致使更多的存儲空間。因此,通常來講,text或者character varying就好了。
Functions
PostgreSQL沒有存儲過程的概念(博主也不明白爲什麼其它數據庫要劃分存儲過程和函數)。函數會返回最後一條語句的結果[的第一行數據];若要返回結果集,須要顯示聲明要返回某類型的結果集或Table。 Unless the function is declared to return void, the last statement must be a SELECT, or an INSERT, UPDATE, or DELETE that has a RETURNING clause. You cannot use transaction control commands, e.g. COMMIT, SAVEPOINT, and some utility commands, e.g. VACUUM, in SQL functions. 函數體以雙"$"符號或單引號包裹,若用單引號包裹則須要注意特殊字符轉義。能夠在函數體內以參數名(9.2及之後版本支持)或"$n"的方式引用參數。舉個例子:
CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS integer AS $$ UPDATE bank SET balance = balance - debit WHERE accountno = tf1.accountno -- 因爲參數名和列同名,前面須要加函數名做爲前綴 -- ;SELECT balance FROM bank WHERE accountno = tf1.accountno; RETURNING balance; $$ LANGUAGE SQL;
在INSERT INTO或者UPDATE的時候在最後面加上RETURNING colname,PostgreSQL會在插入或者更新數據以後會返回你指定的字段。
函數能夠接收、返回多個字段,將這多個字段看做一個總體,稱爲複合類型。好比數據表中的一行,或者使用ROW構造函數構造的一行數據,或者以逗號分隔的多個字段。咱們能夠顯式定義本身的複合類型,如:
CREATE TYPE inventory_item AS ( name text, supplier_id integer, price numeric );
而後就能夠將inventory_item用於不少地方了,甚至將一個表字段類型設置爲inventory_item,以下:
CREATE TABLE on_hand ( item inventory_item, count integer ); INSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000);
在你建立表的時候,也會自動建立一個複合類型,名字與表名字相同,表示該表的複合類型。須要注意的是,表定義的各項約束(如不可爲空)對自動建立的同名複合類型無效。
關於複合類型值寫法,上面的ROW方式比較經常使用,若是是多個字段,那麼ROW能夠省略,即('fuzzy dice', 42, 1.99);還能夠通常格式——'("fuzzy dice",42,1.99)'——外層以單引號包裹。咱們能夠操做複合類型的總體,也能夠針對其某幾個字段操做,具體請參考文檔。
回到函數的介紹,好比下面兩段代碼表示的是同一個意思:
-- 1 CREATE FUNCTION new_emp() RETURNS emp AS $$ SELECT text ’None’ AS name, -- 注意類型轉換 1000.0 AS salary, 25 AS age, point ’(2,2)’ AS cubicle; $$ LANGUAGE SQL; -- 字段順序和類型要和返回類型(此處是emp)保持一致 -- 2 CREATE FUNCTION new_emp() RETURNS emp AS $$ SELECT ROW(’None’, 1000.0, 25, ’(2,2)’)::emp; $$ LANGUAGE SQL;
雙冒號:: 表示類型轉換。
前面說到,函數能返回集合和表,返回表是最近出版的SQL標準之一,因此可能比返回集合更好一點;可是對於返回表來講,It is not allowed to use explicit OUT or INOUT parameters with the RETURNS TABLE notation — you must put all the output columns in the TABLE list.
Polymorphic SQL Functions:運行select array_to_string('{"meeting", "lunch"}',',');報錯:could not determine polymorphic type because input has type "unknown"。緣由:SQL functions can be declared to accept and return the polymorphic types anyelement, anyarray, anynonarray, anyenum, and anyrange.This is required if the argument is just a string literal, since otherwise it would be treated as type unknown。而經過文檔發現array_to_string(anyarray, text [, text]),array_to_string就是Polymorphic SQL Function。so,要麼換成Array形式,要麼顯式類型轉換。
穩定性級別:
PostgreSQL中的函數在定義時有三種穩定性級別:VOLATILE(不穩定)、STABLE(穩定)和IMMUTABLE(很是穩定)。默認狀況下,CREATE FUNCTION建立函數的穩定性爲VOLATILE。穩定性級別使得優化器能夠判斷不一樣函數的行爲。
VOLATILE函數能夠作任何事情,包括修改數據庫。在調用中,輸入一樣的參數會返回不一樣的結果,優化器並不對這一類函數的行爲作任何假設。在一個Query中,對於每一行都會從新計算該函數。
STABLE函數不能修改數據庫,單個Query中全部行給定一樣的參數確保返回相同的結果。這種穩定級別容許優化器將屢次函數調用轉換爲一次。在索引掃描的條件中使用這種函數是可行的,由於索引掃描只計算一次比較值(comparison value),而不是每行都計算一次。
IMMUTABLE函數不能修改數據庫,在任何狀況下,只要輸入參數相同,返回結果就相同。這種級別的函數,優化器能夠提早進行計算,在查詢過程當中做爲常量參數。好比:SELECT...WHERE x=2+2 能夠簡化爲SELECT...WHERE x=4。
爲了獲得最佳的優化結果,在建立函數時咱們應該指定嚴格的穩定性級別。
任何有反作用的函數都應該被標記爲VOLATILE;另外,有些沒有反作用但在一次query中值會發生改變的函數也應該標記爲VOLATILE,好比random(),currval(),timeofday()。current_timestamp類型的函數應該被標記爲STABLE,由於它們的值在同一事務中不會發生改變。
PostgreSQL還支持函數重載。。。,因此pg的函數定位還和所傳參數的個數有關。同時還支持可變參數和參數默認值,某種程度上提升了編寫函數的靈活性,可是仍是有不方便的地方,好比參數默認值,假如一個函數有多個參數具備默認值,調用的時候,我想保留前面的參數默認值不變,只改變後面若干個參數值,那麼也須要傳遞前面不變的和後面變了的參數值,而不像C#同樣,能夠指定參數名傳遞值。因此有些時候仍是得寫不少個重載,或者在函數內部做判斷。
對於返回setof的函數來講,經過select func方式返回的是一列(已逗號分隔的字符串),select * from func返回的纔是表格格式。
動態SQL
有時候咱們會拼接字符串後,再執行該條語句,PostgreSQL也對這種狀況做了支持(注意此時PL/pgSQL's normal attempts to cache plans for commands will not work in such scenarios.執行計劃是每次執行動態語句時臨時作的)。以PL/Pgsql爲例,語法以下:
EXECUTE command-string [ INTO [STRICT] target ] [ USING expression [, ... ] ];
上式中的可選項target表示a record variable, a row variable, or a comma-separated list of simple variables and record/row fields。若是要返回結果集,那麼須要用到RETURN QUERY的一個變形:RETURN QUERY EXECUTE command-string [ USING expression [, ... ] ]; 參數表達式能夠經過USING插入到計算查詢字符串中,以EXECUTE命令的一樣方式。
PostgreSQL也提供了一些字符串處理函數,能夠更方便地拼接字符串。
quote_ident:Return the given string suitably quoted to be used as an identifier in an SQL statement string。在字符串是表名列名等標識數據庫對象時候有用。
quote_literal:Return the given string suitably quoted to be used as a string literal in an SQL statement string.它會對一些特殊字符進行轉義。
quote_nullable:當傳入參數可能爲null時,可以使用quote_nullable,而不是quote_literal。前者返回字符串格式的'Null',後者返回的就是Null。固然了pg中全部東西與null比較返回的都是null,這點須要注意。
format:EXECUTE format('UPDATE tbl SET %I = %L WHERE key = %L', colname, newvalue, keyvalue); or EXECUTE format('UPDATE tbl SET %I = $1 WHERE key = $2', colname) USING newvalue, keyvalue; 後者更有效率,because the parameters newvalue and keyvalue are not converted to text.注意format的格式化類型字符s, I, L. 分別表示字符串, identified, 和literal(注意s、L不要搞反了)。示例:
1 CREATE OR REPLACE FUNCTION func_get_merchandises( 2 keyword text, 3 isinland boolean, 4 startindex integer DEFAULT 0, 5 takecount integer DEFAULT 20, 6 sortfield text DEFAULT 'MerchandiseName'::text, 7 sortorder text DEFAULT 'asc'::text) 8 RETURNS SETOF "Merchandises" AS 9 10 $BODY$ 11 begin 12 return query EXECUTE 13 format('select m.* from "Merchandises" m 14 where m.tsv @@ plainto_tsquery($1) and m."IsInland"=$2 15 order by %I %s limit $3 offset $4',sortfield,sortorder) using keyword,isinland,takecount,startindex; 16 end 17 $BODY$ 18 LANGUAGE plpgsql VOLATILE
須要注意的是第15行sortfield和sortorder不能做爲execute參數放在using後面,不然並不會替換,由於前者是標識變量(如表名列名),後者爲什麼並不很是清楚。using參數適用場景,在文檔中稍有說起。
PostgreSQL安裝與配置
不得不說,自從入門了Linux以後,技術接觸面廣了不少,更樂意嘗試.NET「標配」以外的東西。要在CentOS7.0上安裝PostgreSQL,先到PostgreSQL RPM Building Project - Repository Packages找到對應的RPM包,並用yum安裝:
yum install https://download.postgresql.org/pub/repos/yum/9.5/redhat/rhel-7-x86_64/pgdg-centos95-9.5-2.noarch.rpm
上步只是install了RPM包,then,安裝postgresql-server(還有其它一些packages,不過咱們暫時安裝postgresql-server便可)
yum install postgresql95-server
若是你安裝的是9.4的版本,只要把上面的數字95改爲94便可。
Due to policies for Red Hat family distributions, the PostgreSQL installation will not be enabled for automatic start or have the database initialized automatically. To make your database installation complete, you need to perform these two steps:
/usr/pgsql-9.5/bin/postgresql95-setup initdb #初始化庫 systemctl start postgresql-9.5.service #啓動
爲了其它主機能鏈接到服務器,須要進行一些配置。關於系統參數配置,PostgreSQL提供了多種方式,適用場景稍有不一樣。這裏選擇編輯postgresql.conf文件的方式,另外還有個postgresql.auto.conf文件,保存的是系統參數默認值,是不容許直接編輯的,可使用ALTER SYSTEM 命令進行配置值設定。postgresql.conf存儲在PostgreSQL的data目錄下,data目錄能夠在initdb時指定,以下:
initdb -D /usr/local/pgsql/data
#或者以下
pg_ctl -D /usr/local/pgsql/data initdb
This may be more intuitive if you are using pg_ctl for starting and stopping the server, so that pg_ctl would be the sole command you use for managing the database server instance.
不指定data目錄的話,會默認給你一個,博主這用find命令看到是/var/lib/pgsql/9.5/data/。由於PostgreSQL實例是依賴於data目錄的,因此能夠在一臺機子上開多個實例,每一個實例都有本身的data目錄,配置天然也不一樣;要pg_ctl啓動、中止、重啓等操做時須要帶上data目錄,或者指定PGDATA環境變量,不然不知道針對哪一個實例進行操做。
find / -name postgresql.conf
#輸出 /var/lib/pgsql/9.5/data/postgresql.conf
找到以後,就能夠進行設置了,順便熟悉下vi的操做。
1 vi postgresql.conf #打開,此時爲通常模式 2 /address #定位到listen_addresses 3 0 #或者home鍵,移動光標到該行最前面 4 X #刪除最前面的井號,即取消該行註釋 5 a #A、i、I等皆可,進入編輯模式,將listen_addresses設爲'localhost,開發機IP' 6 <Esc> #返回通常模式 7 4<Enter> #向下移動4行,定位到#port=5432,一樣刪除前面的井號 8 :wq #保存並退出vi
開放端口centos7以後使用firewall:
firewall-cmd --permanent --zone=public --add-port=5432/tcp
另外還要修改pg_hba.conf文件,容許開發機鏈接(竊覺得這裏和postgresql.conf的listen_addresses稍有重複了),這裏就不細說了,注意使用md5方式,表示客戶端須要使用用戶名和密碼(加密)鏈接服務端。重啓PostgresQL。
pg_ctl -D /var/lib/pgsql/9.5/data restart
最後修改默認用戶postgres的密碼。
# sudo -u postgres psql postgres=# ALTER USER postgres WITH PASSWORD 'postgres';
EF CodeFirst with PostgreSQL(暫緩)
目前EF操做PostgreSQL使用的是EntityFramework6.Npgsql,版本爲3.0.5,大部分的數據類型都支持使用linq操做,然而json和array並不在此列(雖然它所基於的Npgsql是支持json和array類型的)。
Npgsql does support TransactionScope,有兩種方法:Include Enlist=true in your connection string, or Call NpgsqlConnection.EnlistTransaction
後記:爲何要選擇PostgreSQL?關係數據庫,博主接觸最多的是SQLSERVER和MYSQL,目前基本上已經告別SQLSERVER,你懂的;MYSQL號稱最流行,這點毋庸置疑,但如此流行的緣由未必是由於最好的,或者最適用的。在MYSQL裏面作遞歸(遞歸不是SQL標準),基本上多少都是個坑,彷佛也不太跟得上時代的腳步,對NoSQL的支持薄弱,若是你說它只要作好關係型數據庫的本分,那麼某些SQL標準尚不支持,好比LATERAL;全文檢索方面功能不足也是一大軟肋 。而PostgreSQL號稱是全球/宇宙最早進的數據庫,雖有誇大其詞之嫌,確實功能比較全面,並且開源,開源協議是MIT,比MYSQL的GPL來得更自由。
Postgres中執行:UPDATE "TTest" SET "CTest"= floor(random()*16+1); 會發現每條記錄的CTest並不一致,可知並不是先生成隨機數再統一賦值,而是逐一輩子成不一樣的隨機數。不知道其它數據庫是怎樣。
postgres遊標:
do $$ declare tn text; curs1 CURSOR for select tablename from pg_tables where schemaname='public'; begin OPEN curs1; loop fetch curs1 into tn; if not found then exit; end if; --RAISE INFO 'VARIABLE: %', tn; EXECUTE 'ALTER TABLE "' || tn || '" OWNER TO masondever'; end loop; close curs1; end; $$language plpgsql;
有用的函數:coalesce、string_agg
視圖分爲普通視圖和物化視圖,物化視圖是物理存在的,能夠認爲是數據庫層的緩存或臨時存儲,當基礎表數據更新時,須要手動刷新。因爲物化視圖是真實存在的,可在其上建索引提升查詢效率。物化視圖建立——CREATE
MATERIALIZED
VIEW ...
關於PL/Pgsql的語法可參看Chapter 40. PL/pgSQL - SQL Procedural Language。文檔中所說的SQL語法相對PL/Pgsql來講,your client application must send each query to the database server, wait for it to be processed, receive and process the results, do some computation, then send further queries to the server. 這應該指的是標準SQL語法沒有變量和控制結構等,致使中間過程處理只能移到外部(好比應用層)。
發現了一個比較坑的地方(使用的是9.4版):select r.ShopId from (select p."ShopId" from "IndexProductInfoes" p) r 是不行的,由於pg對大小寫敏感,列名如有大寫字母,則必須以雙引號括起來,不然會報錯「column r.shopid does not exist」,能夠看到,雖然代碼裏咱們寫的是r.ShopId,但提示信息是shopid,pg自動給轉爲小寫了。如下幾種寫法都是正確的:
一、select r."ShopId" from (select p."ShopId" from "IndexProductInfoes" p) r; --結果列名ShopId
二、select r.ShopId from (select p."ShopId" ShopId from "IndexProductInfoes" p) r; --結果列名shopid
三、select r.shopid from (select p."ShopId" ShopId from "IndexProductInfoes" p) r; --結果列名shopid
參考資料:
轉載請註明本文出處:http://www.cnblogs.com/newton/p/5203957.html