介绍PostgreSQL中的Lateral类型_数据库其它

PostgreSQL 9.3 用了一种新的联合类型! Lateral联合的推出比较低调,但它实现了之前需要使用编写程序才能获得的强大的新查询. 在本文中, 我将会介绍一个在 PostgreSQL 9.2 不可能被实现的渠道转换分析.
什么是 LATERAL 联合?

对此的最佳描述在文档中 可选 FROM 语句清单 的底部:

LATERAL 关键词可以在前缀一个 SELECT FROM 子项. 这能让 SELECT 子项在FROM项出现之前就引用到FROM项中的列. (没有 LATERAL 的话, 每一个 SELECT 子项彼此都是独立的,因此不能够对其它的 FROM 项进行交叉引用.)

当一个 FROM 项包含 LATERAL 交叉引用的时候,查询的计算过程如下: 对于FROM像提供给交叉引用列的每一行,或者多个FROM像提供给引用列的行的集合, LATERAL 项都会使用行或者行的集合的列值来进行计算. 计算出来的结果集像往常一样被加入到联合查询之中. 这一过程会在列的来源表的行或者行的集合上重复进行.

这种计算有一点密集。你可以比较松散的将 LATERAL 联合理解作一个 SQL 的foreach 选择, 在这个循环中 PostgreSQL 将循环一个结果集中的每一行,并将那一行作为参数来执行一次子查询的计算.

我们可以用这个来干些什么?

看看下面这个用来记录点击事件的表结构:
 

CREATE TABLE event (
  user_id BIGINT,
  event_id BIGINT,
  time BIGINT NOT NULL,
  data JSON NOT NULL,
  PRIMARY KEY (user_id, event_id)
)

每一个事件都关联了一个用户,拥有一个ID,一个时间戳,还有一个带有事件属性的JSON blob. 在堆中,这些属性可能包含一次点击的DOM层级, 窗口的标题,会话引用等等信息.

加入我们要优化我们的登录页面以增加注册. 第一步就是要计算看看我们的哪个渠道转换上正在丢失用户.

示例:一个注册流程的个步骤之间的渠道转换率.

假设我们已经在前端配备的装置,来沿着这一流程来记录事件日志,所有的数据都会保存到上述的事件数据表中.[1] 最开始的问题是,我们要计算有多少人查看了我们的主页,而他们之中有百分之多少在那次查看了主页之后的两个星期之内输入了验证信息. 如果我们使用 PostgreSQL 较老的版本, 我们可能需要使用PL/pgSQL这一PostgreSQL内置的过程语言 来编写一些定制的函数. 而在 9.3 中, 我们就可以使用一个 lateral 联合,只用一个搞笑的查询就能计算出结果,不需要任何扩展或者 PL/pgSQL.

 

SELECT
user_id,
view_homepage,
view_homepage_time,
enter_credit_card,
enter_credit_card_time
FROM (
-- Get the first time each user viewed the homepage.
SELECT
user_id,
1 AS view_homepage,
min(time) AS view_homepage_time
FROM event
WHERE
data->>'type' = 'view_homepage'
GROUP BY user_id
) e1 LEFT JOIN LATERAL (
-- For each row, get the first time the user_id did the enter_credit_card
-- event, if one exists within two weeks of view_homepage_time.
SELECT
1 AS enter_credit_card,
time AS enter_credit_card_time
FROM event
WHERE
user_id = e1.user_id AND
data->>'type' = 'enter_credit_card' AND
time BETWEEN view_homepage_time AND (view_homepage_time + 1000*60*60*24*14)
ORDER BY time
LIMIT 1
) e2 ON true

没有人会喜欢30多行的SQL查询,所以让我们将这些SQL分成片段来分析。第一块是一段普通的 SQL:
 

SELECT
  user_id,
  1 AS view_homepage,
  min(time) AS view_homepage_time
FROM event
WHERE
  data->>'type' = 'view_homepage'
GROUP BY user_id

也就是要获取到每个用户最开始触发 view_homepage 事件的时间. 然后我们的 lateral 联合就可以让我们迭代结果集的每一行,并会在接下来执行一次参数化的子查询. 这就等同于针对结果集的每一行都要执行一边下面的这个查询:
 

SELECT
  1 AS enter_credit_card,
  time AS enter_credit_card_time
FROM event
WHERE
  user_id = e1.user_id AND
  data->>'type' = 'enter_credit_card' AND
  time BETWEEN view_homepage_time AND (view_homepage_time + 1000*60*60*24*14)
ORDER BY time
LIMIT 1

例如,对于每一个用户,要获取他们在触发 view_homepage_time 事件后的两星期内触发 enter_credit_card  事件的时间. 因为这是一个lateral联合,我们的子查询就可以从之前的子查询出引用到 view_homepage_time 结果集. 否则,子查询就只能单独执行,而没办法访问到另外一个子查询所计算出来的结果集.

之后哦我们整个封装成一个select,它会返回像下面这样的东西:

user_id | view_homepage | view_homepage_time | enter_credit_card | enter_credit_card_time
---------+---------------+--------------------+-------------------+------------------------
567 | 1 | 5234567890 | 1 | 5839367890
234 | 1 | 2234567890 | |
345 | 1 | 3234567890 | |
456 | 1 | 4234567890 | |
678 | 1 | 6234567890 | |
123 | 1 | 1234567890 | |
... 

因为这是一个左联合,所以查询结果集中会有不匹配 enter_credit_card 事件的行,只要有 view_homepage 事件就行. 如果我们汇总所有的数值列,就会得到渠道转换的一个清晰汇总:
 

SELECT
  sum(view_homepage) AS viewed_homepage,
  sum(enter_credit_card) AS entered_credit_card
FROM (
  -- Get the first time each user viewed the homepage.
  SELECT
  user_id,
  1 AS view_homepage,
  min(time) AS view_homepage_time
  FROM event
  WHERE
  data->>'type' = 'view_homepage'
  GROUP BY user_id
) e1 LEFT JOIN LATERAL (
  -- For each (user_id, view_homepage_time) tuple, get the first time that
  -- user did the enter_credit_card event, if one exists within two weeks.
  SELECT
  1 AS enter_credit_card,
  time AS enter_credit_card_time
  FROM event
  WHERE
  user_id = e1.user_id AND
  data->>'type' = 'enter_credit_card' AND
  time BETWEEN view_homepage_time AND (view_homepage_time + 1000*60*60*24*14)
  ORDER BY time
  LIMIT 1
) e2 ON true

… 它会输出:

 viewed_homepage | entered_credit_card
-----------------+---------------------
827 | 10

我们可以向这个渠道中填入带有更多lateral联合的中间步骤,来得到流程中我们需要重点改进的部分. 让我们在查看主页和输入验证信息之间加入对使用示例步骤的查询.
 

SELECT
  sum(view_homepage) AS viewed_homepage,
  sum(use_demo) AS use_demo,
  sum(enter_credit_card) AS entered_credit_card
FROM (
  -- Get the first time each user viewed the homepage.
  SELECT
  user_id,
  1 AS view_homepage,
  min(time) AS view_homepage_time
  FROM event
  WHERE
  data->>'type' = 'view_homepage'
  GROUP BY user_id
) e1 LEFT JOIN LATERAL (
  -- For each row, get the first time the user_id did the use_demo
  -- event, if one exists within one week of view_homepage_time.
  SELECT
  user_id,
  1 AS use_demo,
  time AS use_demo_time
  FROM event
  WHERE
  user_id = e1.user_id AND
  data->>'type' = 'use_demo' AND
  time BETWEEN view_homepage_time AND (view_homepage_time + 1000*60*60*24*7)
  ORDER BY time
  LIMIT 1
) e2 ON true LEFT JOIN LATERAL (
  -- For each row, get the first time the user_id did the enter_credit_card
  -- event, if one exists within one week of use_demo_time.
  SELECT
  1 AS enter_credit_card,
  time AS enter_credit_card_time
  FROM event
  WHERE
  user_id = e2.user_id AND
  data->>'type' = 'enter_credit_card' AND
  time BETWEEN use_demo_time AND (use_demo_time + 1000*60*60*24*7)
  ORDER BY time
  LIMIT 1
) e3 ON true

这样就会输出:

 viewed_homepage | use_demo | entered_credit_card
-----------------+----------+---------------------
827 | 220 | 86 

从查看主页到一周之内使用demo,再到一周以内向其输入信用卡信息,这就向我们提供了三个步骤的通道转换. 从此,功能强大的 PostgreSQL 使得我们可以深入分析这些数据结果集,并对我们的网站性能进行整体的分析. 接着我们可能会有下面这些问题要解决:

  •     使用demo是否能增加注册的可能性?
  •     通过广告找到我们主页的用户是否同来自其他渠道的用户拥有相同的转换率?
  •     转换率会跟随不同的 A/B 测试变量发生怎样的变化?

这些问题的答案会直接影响到产品的改进,它们可以从 PostgreSQL 数据库中找到答案,因为现在它支持 lateral 联合.

没有 lateral 联合,我们就只能借助 PL/pgSQL 来做这些分析。或者,如果我们的数据集很小,我们可能就不会碰这些复杂、低效的查询. 在一项探索性数据研究使用场景下,你可能只是将数据从 PostgreSQL 里面抽取出来,并使用你所选择的脚本语言来对其进行分析。但是其实还存在更强大的理由来用SQL表述这些问题, 特别是如果你正想要把整个全封装到一套易于理解的UI中,并向非技术型用户发布功能 的时候.

注意这些查询可以被优化,以变得更加高效. 在本例中,如果我们在 (user_id, (data->>'type'), time)上创建一个btree索引, 我们只用一次索引查找就能针对每一个用户计算每一个渠道步骤. 如果你使用的是SSD,在上面做查找花费是很小的,那这就足够了。而如果不是,你就可能需要用稍微不同的手段来图示化你的数据,详细的内容我会留到另外一篇文章之中进行介绍.

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索postgresql
postgresql lateral、postgresql恢复数据库、postgresql查看数据库、postgresql创建数据库、postgresql删除数据库,以便于您获取更多的相关知识。

时间: 2024-10-03 04:54:57

介绍PostgreSQL中的Lateral类型_数据库其它的相关文章

介绍PostgreSQL中的jsonb数据类型_数据库其它

PostgreSQL 9.4 正在加载一项新功能叫jsonb,是一种新型资料,可以储存支援GIN索引的JSON 资料.换言之,此功能,在即将来临的更新中最重要的是,如果连这都不重要的话,那就把Postgres 置于文件为本数据库系统的推荐位置吧. 自从9.2开始,一个整合JSON 资料类型已经存在,带有一整套功能(例如资料产生和资料解构功能),还有9.3新增的操作者.当使用JSON 资料类型,资料的被存储成一完全一样的副本,功能还在此之上运作,还另外需要后台运作的重新分析. 这心得JSONB 资

介绍PostgreSQL中的范围类型特性_数据库其它

 PostgreSQL 9.2 的一项新特性就是范围类型 range types,通过这个名字你可以轻松猜出该类型的用途,它可让你为某列数据定义数值范围. 这个简单的特性可以让我们不需要定义两个字段来描述数值的开始值和结束值,一个最直观的例子就是:   postgres# CREATE TABLE salary_grid (id int, position_name text, start_salary int, end_salary int); CREATE TABLE postgres# I

一个提升PostgreSQL性能的小技巧_数据库其它

 在一个(差)的PostgreSQL 查询中只要一个小小到改动(ANY(ARRAY[...])to ANY(VALUES(...)))就能把查询时间从20s缩减到0.2s.从最简单的学习使用 EXPLAIN ANALYZE开始,到学习使用 Postgres community 大量学习时间的投入将有百倍时间到回报. 使用Postgres监测慢的Postgres查询 在这周早些时候,一个用于我们的图形编辑器上的小表(10GB,1500万行)的主键查询,在我们的一个(多个)数据库上发生来大的查询性能

设置CA证书来强化PostgreSQL的安全性的教程_数据库其它

在经历了多次的摸索实验后我终于成功地实现了SSL证书认证的功能,因此我想这次我要把这些步骤记录下来供日后查阅. 出于安全和方便的原因,我要在一台单独的专用机器上签署客户的证书,这台机器也称为 证书授证中心(CA). 这让我们在授权新的客户端时不必先登录到PostgreSQL服务器然后再签署证书或者修改pg_hba.conf. 我们要创建一个特殊的数据库组,叫sslcertusers.这个组里的所有用户都可以通过由CA签署的证书进行连接. 在下面的例子中,请将"trustly"替换成你的

举例讲解如何判断JavaScript中对象的类型_基础知识

在编写js程序的过程中,可能常常需要判断一个对象的类型,例如你写了一个函数,需要通过判断不同的参数类型来编写不同的代码. 首先可能会想到typeof操作符,看下面的这个例子: <script type="text/javascript"> var object = {}; var b = true; alert(typeof object + " " + typeof b); </script> 得到的结果如下: 从上面的结果可以看到,用ty

mysql &amp;quot;group by&amp;quot;与&amp;quot;order by&amp;quot;的研究--分类中最新的内容_数据库其它

这两天让一个数据查询难了.主要是对group by 理解的不够深入.才出现这样的情况 这种需求,我想很多人都遇到过.下面是我模拟我的内容表 复制代码 代码如下: CREATE TABLE `test` ( `id` INT(10) NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL, `category_id` INT(10) NOT NULL, `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMES

postgresql中比较real类型相等的正确方法

先说结论,正确的方法类似如下语句: select vat from expenses where round(vat::numeric, 2) = 19.66; 而不能直接用相等: select vat from expenses where vat = 19.66; 参考: http://www.peterbe.com/plog/comparing-real-values 当你遇到诸如0.01的值时直接使用=则得不到你想要的结果.

postgresql sql批量更新记录_数据库其它

复制代码 代码如下: CREATE FUNCTION updateTchrNm() RETURNS void AS $body$ DECLARE rownum integer := 1; BEGIN while rownum <= 1000 LOOP     update t_tchr set tchr_nm = '田中愛子' || rownum, tchr_knm = 'タナカアイコ' || rownum, tchr_anm = 'tanaka' || rownum where tchr_cd

postgresql-PostgreSQL 中的json类型字段查询

问题描述 PostgreSQL 中的json类型字段查询 PostgreSQL 中的json类型字段查询,该字段只有 '[]',就是空值,怎么查询是这些的数据 解决方案 这不是空值,是个空数组,判断该数组的 length为零即可.