Postgresql 需要一些关于SQL的提示/建议(包括命令和一些优化)

Postgresql 需要一些关于SQL的提示/建议(包括命令和一些优化),postgresql,Postgresql,我一直在寻找一种方法,从PostgreSQL中的GROUP BY中选择第一项,直到我发现这个stackoverflow: 在那里,我看到使用了WITH命令。 我试图理解SQL的一些更“高级”的命令,如分区,带,行号等。直到两三个月前,我只知道基本的命令(选择,内部联接,左联接,按顺序,按顺序分组等) 我有一个小问题(已解决,但我不知道这是否是更好的方法) *better way=我更关心的是干净的SQL代码,而不是性能-这只适用于每天执行一次且不超过5000条记录的报表 在PostgreSQL中

我一直在寻找一种方法,从PostgreSQL中的
GROUP BY
中选择第一项,直到我发现这个stackoverflow:

在那里,我看到使用了
WITH
命令。 我试图理解SQL的一些更“高级”的命令,如
分区
行号
等。直到两三个月前,我只知道基本的命令(
选择
内部联接
左联接
按顺序
按顺序
分组等)

我有一个小问题(已解决,但我不知道这是否是更好的方法)

*better way=我更关心的是干净的SQL代码,而不是性能-这只适用于每天执行一次且不超过5000条记录的报表

在PostgreSQL中,我有两个表:

+----------------------------------------------+
| TABLE NAME: point                            |
+--------+---------------+----------+----------+
|     km |      globalid |      lat |     long |
+--------+---------------+----------+----------+
|  36600 | 1553E2AB-B2F8 | -1774.44 | -5423.58 |
| 364000 | 25EB2465-1B8A | -1773.42 | -5422.03 |
| 362000 | 5FFDE611-88DF | -1771.80 | -5420.37 |
+--------+---------------+----------+----------+


+---------------------------------------------------------+
| TABLE NAME: photo                                       |
+--------------+---------------+------------+-------------+
| attachmentid |  rel_globalid |       date |    filename |
+--------------+---------------+------------+-------------+
|            1 | 1553E2AB-B2F8 | 2015-02-24 | photo01.jpg |
|            2 | 1553E2AB-B2F8 | 2015-02-24 | photo02.jpg |
|          405 | 25EB2465-1B8A | 2015-02-12 | photo03.jpg |
|          406 | 25EB2465-1B8A | 2015-02-12 | photo04.jpg |
|          407 | 25EB2465-1B8A | 2015-02-13 | photo06.jpg |
|            3 | 5FFDE611-88DF | 2015-02-12 | photo07.jpg |
+--------------+---------------+------------+-------------+
因此,对于问题:

每个
都有一张或多张照片,但我只需要
数据,以及第一张最后一张
照片
。如果
只有一张
照片
,我只需要第一张
照片
。如果
有三张
照片
,我只需要第一张和第三张
照片

因此,我决定:

首先,我需要每个
点的第一张
照片
,因此,我按
rel\u globalid
分组,并按组对每张照片编号:

WITH photos_numbered AS (
    SELECT
      rel_globalid,
      date,
      filename,
      ROW_NUMBER()
      OVER (
        PARTITION BY rel_globalid
        ORDER BY date
      ) AS photo_num
    FROM
      photo
)
有了这段代码,我也可以得到第二、第三等

好的,现在,我想得到第一张照片(仍然使用上面的
):

为了获得最后一张照片,我使用了以下SQL:

SELECT
  p1.*
FROM
  photos_numbered p1
JOIN (
  SELECT
    rel_globalid,
    max(photo_num) photo_num
  FROM
    photos_numbered
  GROUP BY
    rel_globalid
  ) p2
  ON
    p1.rel_globalid = p2.rel_globalid AND
    p1.photo_num = p2.photo_num
WHERE
  p1.photo_num > 1
其中p1.photo_num>1
是因为如果
只有一张
照片
,则该
照片
将显示为第一张照片,最后一张照片将为

好的,现在我必须将第一张
照片的
选择
和最后一张
照片的
转换为带有
,并对第一张
照片的
内部连接和最后一张
照片的
左连接执行一个简单的
选择

WITH photos_numbered AS (
    SELECT
      rel_globalid,
      date,
      filename,
      ROW_NUMBER()
      OVER (
        PARTITION BY rel_globalid
        ORDER BY date
      ) AS photo_num
    FROM
      photo
), first_photo AS (
    SELECT *
    FROM
      photos_numbered
    WHERE
      photo_num = 1
), last_photo AS (
    SELECT p1.*
    FROM
      photos_numbered p1
      JOIN (
             SELECT
               rel_globalid,
               max(photo_num) photo_num
             FROM
               photos_numbered
             GROUP BY
               rel_globalid
           ) p2
        ON p1.rel_globalid = p2.rel_globalid AND
           p1.photo_num = p2.photo_num
    WHERE
      p1.photo_num > 1
)
SELECT DISTINCT
  point.km,
  point.globalid,
  point.lat,
  point."long",
  first_photo.date     AS fp_date,
  first_photo.filename AS fp_filename,
  last_photo.date      AS lp_date,
  last_photo.filename  AS lp_filename
FROM
  point
  INNER JOIN
  first_photo
    ON
      first_photo.rel_globalid = point.globalid
  LEFT JOIN
  last_photo
    ON
      last_photo.rel_globalid = point.globalid
ORDER BY
  km
我认为这个SQL对于一件“简单的事情”来说是巨大的

他在工作吗?是的,但是我想要一些建议,一些我可以更好地阅读和理解的文档,一些我可以用来制作“更好”SQL的命令(就像我说的,大约两三个月前我甚至不知道
分区
命令)


我试图在这里为SQLFiddle添加一个链接,但SQLFiddle从未对我起作用(始终返回“oops”消息)。

如果您正在寻找干净的SQL,请尝试使用第一个\u值和最后一个\u值窗口函数进行横向左连接,而不是使用公共表表达式(with子句):

当只有一条记录时,可以使用带有大小写表达式的附加
count(*)over()
来“清除”最后一张照片的值:

select *
from point po
left join lateral 
(
   select first_value( date )     over( order by ph.date) as first_photo_date,
          first_value( filename ) over( order by ph.date) as first_photo_filename,
          case when count(*) over () > 1 
               then last_value( date )    over( order by ph.date)
          end as last_photo_date,
          case when count(*) over () > 1 
                then last_value( filename )  over( order by ph.date) 
          end as last_photo_filename    
   from photo ph
   where po.globalid = ph.rel_globalid 
   limit 1
) q on true
;

如果要查找干净的SQL,请尝试使用第一个\u值和最后一个\u值窗口函数进行横向左联接,而不是使用公共表表达式(with子句):

当只有一条记录时,可以使用带有大小写表达式的附加
count(*)over()
来“清除”最后一张照片的值:

select *
from point po
left join lateral 
(
   select first_value( date )     over( order by ph.date) as first_photo_date,
          first_value( filename ) over( order by ph.date) as first_photo_filename,
          case when count(*) over () > 1 
               then last_value( date )    over( order by ph.date)
          end as last_photo_date,
          case when count(*) over () > 1 
                then last_value( filename )  over( order by ph.date) 
          end as last_photo_filename    
   from photo ph
   where po.globalid = ph.rel_globalid 
   limit 1
) q on true
;

使用krokodilko的答案,我进行了一个新的SQL查询,没有
左连接横向
,因为我使用的是PostgreSQL 9.2(没有
左连接横向


我唯一不喜欢的是
OVER(PARTITION)
在几乎每个
字段中
内部连接中

使用krokodilko的答案,我做了一个新的SQL查询,没有
左连接横向
,因为我使用的是PostgreSQL 9.2(没有
左连接横向


我唯一不喜欢它的是
内部连接中的几乎每个
字段中的
上(分区)
!左侧连接对我来说是新的!不幸的是,它与这里运行的postgresql 9.2不兼容(不要问我为什么运行这个版本)!tks!左侧连接对我来说是新的!不幸的是,它与这里运行的postgresql 9.2不兼容(不要问我为什么运行这个版本)!
select *
from point po
left join lateral 
(
   select first_value( date )     over( order by ph.date) as first_photo_date,
          first_value( filename ) over( order by ph.date) as first_photo_filename,
          case when count(*) over () > 1 
               then last_value( date )    over( order by ph.date)
          end as last_photo_date,
          case when count(*) over () > 1 
                then last_value( filename )  over( order by ph.date) 
          end as last_photo_filename    
   from photo ph
   where po.globalid = ph.rel_globalid 
   limit 1
) q on true
;
SELECT DISTINCT
  po.km,
  po.globalid,
  po.lat,
  po."long",
  ph.fp_date,
  ph.fp_filename,
  ph.lp_date,
  ph.lp_filename
FROM
  point po
INNER JOIN
  (
    SELECT DISTINCT
      rel_globalid,
      first_value(date) OVER (PARTITION BY ph.rel_globalid) AS fp_date,
      first_value(filename) OVER (PARTITION BY ph.rel_globalid) AS fp_filename,
      CASE WHEN count(*) OVER (PARTITION BY ph.rel_globalid) > 1 THEN 
        last_value(date) OVER (PARTITION BY ph.rel_globalid)
      END AS lp_date,
      CASE WHEN count(*) OVER (PARTITION BY ph.rel_globalid) > 1 THEN 
        last_value(filename) OVER (PARTITION BY ph.rel_globalid)
      END AS lp_filename
    FROM
      photo ph
    ORDER BY
      rel_globalid
  ) ph
  ON ph.rel_globalid = po.globalid