简化具有多个WITH和多个子查询的查询

时间:2012-09-25 18:21:14

标签: sql sql-server sql-server-2008 tsql

令我困扰的是,对于一个简单的查询,我必须写出这么多的子选择和WITH语句。

问题是:是否有关于如何简化具有子查询的查询的基本指南

这是我的疑问:

WITH cte_min
     AS (SELECT a.client_id,
                a.specimen_source,
                a.received_date
         FROM   f_accession_daily a
                JOIN (SELECT DISTINCT f.client_id,
                                      f.received_date,
                                      f.accession_daily_key
                      FROM   F_ACCESSION_DAILY f
                             JOIN (SELECT CLIENT_ID,
                                          Min(received_date) MinRecDate
                                   FROM   F_ACCESSION_DAILY
                                   GROUP  BY CLIENT_ID) i
                               ON f.CLIENT_ID = i.CLIENT_ID
                                  AND f.RECEIVED_DATE = i.MinRecDate) b
                  ON a.ACCESSION_DAILY_KEY = b.ACCESSION_DAILY_KEY),
     cte_max
     AS (SELECT a.client_id,
                a.specimen_source,
                a.received_date
         FROM   f_accession_daily a
                JOIN (SELECT DISTINCT f.client_id,
                                      f.received_date,
                                      f.accession_daily_key
                      FROM   F_ACCESSION_DAILY f
                             JOIN (SELECT CLIENT_ID,
                                          Max(received_date) MaxRecDate
                                   FROM   F_ACCESSION_DAILY
                                   GROUP  BY CLIENT_ID) i
                               ON f.CLIENT_ID = i.CLIENT_ID
                                  AND f.RECEIVED_DATE = i.MaxRecDate) b
                  ON a.ACCESSION_DAILY_KEY = b.ACCESSION_DAILY_KEY),
     cte_est
     AS (SELECT DISTINCT client_id,
                         MLIS_DATE_ESTABLISHED
         FROM   D_CLIENT
         WHERE  REC_ACTIVE_FLG = 1
                AND MLIS_DATE_ESTABLISHED IS NOT NULL)
SELECT DISTINCT f.client_id,
                cmin.specimen_source,
                cmin.received_date,
                cmax.specimen_source,
                cmax.received_date,
                cest.MLIS_DATE_ESTABLISHED
FROM   F_ACCESSION_DAILY f
       LEFT JOIN cte_max cmax
         ON cmax.CLIENT_ID = f.CLIENT_ID
       LEFT JOIN cte_min cmin
         ON cmin.CLIENT_ID = f.CLIENT_ID
       LEFT JOIN cte_est cest
         ON cest.CLIENT_ID = f.CLIENT_ID 

我并不是要求你自己进行简化(虽然我会非常感激),而是我要求重新编写此查询的一般指导方针/指示更优雅。

3 个答案:

答案 0 :(得分:3)

这看起来更好吗?

;WITH minmax AS (
SELECT client_id, specimen_source, received_date,
       RMin = row_number() over (partition by Client_id
                                 order by received_date, accession_daily_key),
       RMax = row_number() over (partition by Client_id
                                 order by received_date desc, accession_daily_key desc)
FROM F_ACCESSION_DAILY
)
SELECT f.client_id,
       max(case when rmin=1 then f.specimen_source end),
       max(case when rmin=1 then f.received_date end),
       max(case when rmax=1 then f.specimen_source end),
       max(case when rmax=1 then f.received_date end),
       D.MLIS_DATE_ESTABLISHED
FROM   minmax f
LEFT JOIN D_CLIENT D ON D.REC_ACTIVE_FLG = 1 AND D.MLIS_DATE_ESTABLISHED IS NOT NULL
WHERE 1 in (f.rmin, f.rmax)
GROUP BY f.client_id, D.MLIS_DATE_ESTABLISHED

答案 1 :(得分:1)

50行报告5个值,并且在所有这些中仅引用了两个表。

在第一个CTE中,您有4个连接(或虚拟连接)到同一个表,没有其他表涉及报告3列。不知道关键所以不能得出结论可以减少。

如果cte不是多次引用,那么它不会导致更少的代码行。

对于其中一个,这个cte可以用更少的代码替换。

cte_est
     AS (SELECT DISTINCT client_id,
                         MLIS_DATE_ESTABLISHED
         FROM   D_CLIENT
         WHERE  REC_ACTIVE_FLG = 1
                AND MLIS_DATE_ESTABLISHED IS NOT NULL)
...
cest.MLIS_DATE_ESTABLISHED
...
LEFT JOIN cte_est cest
         ON cest.CLIENT_ID = f.CLIENT_ID

缩减为

D_CLIENT.MLIS_DATE_ESTABLISHED
...
LEFT JOIN  D_CLIENT 
       ON  D_CLIENT.CLIENT_ID = f.CLIENT_ID 
       AND D_CLIENT.REC_ACTIVE_FLG = 1
       AND D_CLIENT.MLIS_DATE_ESTABLISHED IS NOT NULL

答案 2 :(得分:1)

虽然我不确定每个人是否会认为这更简单和/或更容易阅读,但我会这样做:

WITH 
  cte_MaxMinRecvd As
(
    SELECT      CLIENT_ID,   
                Min(received_date) MinRecDate,
                Max(received_date) MaxRecDate
    FROM        F_ACCESSION_DAILY
    GROUP BY    CLIENT_ID
)
, cte_MaxMinDaily As
(
    SELECT      *
    FROM        F_ACCESSION_DAILY f
    JOIN        cte_MaxMinRecvd   i     ON  f.CLIENT_ID = i.CLIENT_ID
)
, cte_min AS 
(
    SELECT  a.client_id,
            a.specimen_source,
            a.received_date
    FROM    F_ACCESSION_DAILY a
    WHERE   EXISTS(
                    SELECT  *
                    FROM    cte_MaxMinDaily f
                    WHERE   f.RECEIVED_DATE       = f.MinRecDate
                      AND   a.ACCESSION_DAILY_KEY = f.ACCESSION_DAILY_KEY
                   )
)
, cte_max AS 
(
    SELECT  a.client_id,
            a.specimen_source,
            a.received_date
    FROM    f_accession_daily a
    WHERE   EXISTS(
                    SELECT  *
                    FROM    cte_MaxMinDaily f
                    WHERE   f.RECEIVED_DATE       = f.MinRecDate
                      AND   a.ACCESSION_DAILY_KEY = f.ACCESSION_DAILY_KEY
                   )
)
SELECT DISTINCT 
            f.client_id,
            cmin.specimen_source,
            cmin.received_date,
            cmax.specimen_source,
            cmax.received_date,
            cest.MLIS_DATE_ESTABLISHED
FROM        F_ACCESSION_DAILY f
LEFT JOIN   cte_max  cmax        ON  cmax.CLIENT_ID = f.CLIENT_ID
LEFT JOIN   cte_min  cmin        ON  cmin.CLIENT_ID = f.CLIENT_ID
LEFT JOIN   D_CLIENT cest        ON  cest.CLIENT_ID = f.CLIENT_ID 
                                 AND cest.REC_ACTIVE_FLG = 1
                                 AND cest.MLIS_DATE_ESTABLISHED IS NOT NULL

主要是我做的是

  1. 将大多数子查询转换为CTE(如果适用)
  2. 将Min和Max子查询合并在一起,
  3. 将DISTINCT子查询更改为EXISTS子查询,这可以更简单(通常表现更好)
  4. 哎呀,我也摆脱了Blam建议的cte_est CTE。