PostgreSQL反模式:“只剩下一个!”

在SQL中,您描述要获得的“内容”,而不是应如何执行。因此,以“边听边写”的方式开发SQL查询的问题,连同在SQL中计算条件特殊性,一应俱全



今天,通过使用非常简单的示例,让我们看看在使用的背景下GROUP/DISTINCT以及LIMIT与它们一起可以带来什么



现在,如果您在请求中写到“首先连接这些板,然后丢弃所有重复的板,则每个密钥应该只有一个副本” –即使完全不需要连接,也是如此。



有时您很幸运,而且“可以正常使用”,有时会对性能产生不愉快的影响,有时从开发人员的角度来看,它绝对带来了意外的影响。





好吧,也许不是那么壮观,但是...



“甜蜜的情侣”:JOIN + DISTINCT



SELECT DISTINCT
  X.*
FROM
  X
JOIN
  Y
    ON Y.fk = X.pk
WHERE
  Y.bool_condition;


多么清楚我们想要选择这样的记录X,其中Y具有与满足条件相关的记录我们通过写一个请求JOIN-几次获得了一些pk值(实际上是Y中有多少个匹配记录)。如何删除?当然可以DISTINCT



当每个X记录有几百个链接的Y记录,然后英勇地删除重复项时,这尤其令人“高兴”……该







如何解决?首先,请意识到可以将任务修改为“选择Y具有与满足条件至少相关的Y的记录X” -毕竟,我们不需要Y记录本身。



嵌套的存在



SELECT
  *
FROM
  X
WHERE
  EXISTS(
    SELECT
      NULL
    FROM
      Y
    WHERE
      fk = X.pk AND
      bool_condition
    LIMIT 1
  );


某些PostgreSQL版本知道足以在EXISTS中找到第一个可用记录,而较旧的版本则不行。因此,我更喜欢始终指定LIMIT 1inside EXISTS



横向联接



SELECT
  X.*
FROM
  X
, LATERAL (
    SELECT
      Y.*
    FROM
      Y
    WHERE
      fk = X.pk AND
      bool_condition
    LIMIT 1
  ) Y
WHERE
  Y IS DISTINCT FROM NULL;


如有必要,相同的选项允许同时立即从找到的链接Y记录中返回一些数据。在文章“ PostgreSQL反模式:罕见的条目将飞到JOIN的中间”中讨论了类似的选项


“为什么要多付钱”:DISTINCT [ON] + LIMIT 1



这种查询转换的另一个优点是,如果只需要记录中的一个/几个,就可以轻松地限制记录的迭代次数,如下所示:



SELECT DISTINCT ON(X.pk)
  *
FROM
  X
JOIN
  Y
    ON Y.fk = X.pk
LIMIT 1;


现在,我们阅读了该请求,并试图了解DBMS打算做什么:



  • 我们连接板
  • 由X.pk唯一
  • 从其余记录中选择一个


也就是说,您得到了什么?唯一记录的“一个记录” -如果您采用非唯一记录的一个,结果会有所改变吗?..“如果没有区别,为什么还要支付更多?”



SELECT
  *
FROM
  (
    SELECT
      *
    FROM
      X
    --     
    LIMIT 1 -- +1 Limit
  ) X
JOIN
  Y
    ON Y.fk = X.pk
LIMIT 1;


与主题完全相同GROUP BY + LIMIT 1



“我只是问”:隐式GROUP + LIMIT



在执行请求期间,对板或CTE的非空性 进行各种检查时会遇到类似的情况



...
CASE
  WHEN (
    SELECT
      count(*)
    FROM
      X
    LIMIT 1
  ) = 0 THEN ...


聚集函数(count/min/max/sum/...)即使没有明确指示也可以在整个集合上成功执行GROUP BY只有LIMIT他们对他们不是很友好。



开发人员可能会认为“如果那里有记录,那么我就不再需要LIMIT”但是不要!因为对于基数是:



  • 计算所有记录中的所需内容
  • 给你尽可能多的行


根据目标条件,在此处进行以下替换之一是合适的:



  • (count + LIMIT 1) = 0 NOT EXISTS(LIMIT 1)
  • (count + LIMIT 1) > 0 EXISTS(LIMIT 1)
  • count >= N (SELECT count(*) FROM (... LIMIT N))


“悬挂多少克”:DISTINCT + LIMIT



SELECT DISTINCT
  pk
FROM
  X
LIMIT $1


天真的开发人员可以诚实地相信,只要我们发现遇到的第一个不同的值中有1美元,查询就会停止



将来的某个时候,由于新的“索引跳过扫描”节点(目前正在努力实施该工作,但尚未进行),因此该方法可能并将继续有效。



到目前为止,首先,将检索,唯一记录所有记录,并且仅从记录中返回所请求的记录数。如果我们想要像$ 1 = 4这样的表,并且表中有成千上万的记录,这尤其令人难过



为了不白费事,我们将使用PostgreSQL Wiki上的递归查询“穷人的DISTINCT”






All Articles