本文针对目前最新版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