生成的查询过多的子查询表现不佳

时间:2014-02-27 16:18:31

标签: sql sql-server

我有一个我继承的查询的野兽表现不佳。它由代码生成,然后运行:

 WITH 
    cte_training AS (SELECT DISTINCT rid FROM _training),
    cte_00_0000_s AS (SELECT DISTINCT rid, 'Yes' AS vtbd FROM _training WHERE ISDATE(vocational_training_begin_date) = 1 AND Training_program_SOC='00-0000'),
    cte_00_0000_e AS (SELECT DISTINCT rid, 'Yes' AS vted FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND vocational_training_completed = 1 AND Training_program_SOC='00-0000'),
    cte_00_0000_d AS (SELECT DISTINCT rid, 'Yes' AS vdr FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND vocational_degree_received is not NULL AND Training_program_SOC = '00-0000'),
    cte_00_0000_c AS (SELECT DISTINCT rid, 'Yes' AS vcr FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND Vocational_license_received IS NOT NULL AND Training_program_SOC = '00-0000'),

    cte_11_9111_s AS (SELECT DISTINCT rid, 'Yes' AS vtbd FROM _training WHERE ISDATE(vocational_training_begin_date) = 1 AND Training_program_SOC='11-9111'),
    cte_11_9111_e AS (SELECT DISTINCT rid, 'Yes' AS vted FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND vocational_training_completed = 1 AND Training_program_SOC='11-9111'),
    cte_11_9111_d AS (SELECT DISTINCT rid, 'Yes' AS vdr FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND vocational_degree_received is not NULL AND Training_program_SOC = '11-9111'),
    cte_11_9111_c AS (SELECT DISTINCT rid, 'Yes' AS vcr FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND Vocational_license_received IS NOT NULL AND Training_program_SOC = '11-9111'),

...

    cte_99_9999_s AS (SELECT DISTINCT rid, 'Yes' AS vtbd FROM _training WHERE ISDATE(vocational_training_begin_date) = 1 AND Training_program_SOC='99-9999'),
    cte_99_9999_e AS (SELECT DISTINCT rid, 'Yes' AS vted FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND vocational_training_completed = 1 AND Training_program_SOC='99-9999'),
    cte_99_9999_d AS (SELECT DISTINCT rid, 'Yes' AS vdr FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND vocational_degree_received is not NULL AND Training_program_SOC = '99-9999'),
    cte_99_9999_c AS (SELECT DISTINCT rid, 'Yes' AS vcr FROM _training WHERE ISDATE(vocational_training_end_date) = 1 AND Vocational_license_received IS NOT NULL AND Training_program_SOC = '99-9999')

SELECT cte_training.rid, 
        ISNULL(cte_00_0000_s.vtbd,'No') [00-0000 Started], 
        ISNULL(cte_00_0000_e.vted,'No') [00-0000 Completed],
        ISNULL(cte_00_0000_d.vdr, 'No') [00-0000 Degree],
        ISNULL(cte_00_0000_c.vcr, 'No') [00-0000 Certificate],

        ISNULL(cte_11_9111_s.vtbd,'No') [11-9111 Started], 
        ISNULL(cte_11_9111_e.vted,'No') [11-9111 Completed],
        ISNULL(cte_11_9111_d.vdr, 'No') [11-9111 Degree],
        ISNULL(cte_11_9111_c.vcr, 'No') [11-9111 Certificate],

...

        ISNULL(cte_99_9999_s.vtbd,'No') [99-9999 Started], 
        ISNULL(cte_99_9999_e.vted,'No') [99-9999 Completed],
        ISNULL(cte_99_9999_d.vdr, 'No') [99-9999 Degree],
        ISNULL(cte_99_9999_c.vcr, 'No') [99-9999 Certificate]

FROM cte_training
LEFT OUTER JOIN cte_00_0000_s ON cte_training.rid = cte_00_0000_s.rid
LEFT OUTER JOIN cte_00_0000_e ON cte_training.rid = cte_00_0000_e.rid
LEFT OUTER JOIN cte_00_0000_d ON cte_training.rid = cte_00_0000_d.rid
LEFT OUTER JOIN cte_00_0000_c ON cte_training.rid = cte_00_0000_c.rid

LEFT OUTER JOIN cte_11_9111_s ON cte_training.rid = cte_11_9111_s.rid
LEFT OUTER JOIN cte_11_9111_e ON cte_training.rid = cte_11_9111_e.rid
LEFT OUTER JOIN cte_11_9111_d ON cte_training.rid = cte_11_9111_d.rid
LEFT OUTER JOIN cte_11_9111_c ON cte_training.rid = cte_11_9111_c.rid

...

LEFT OUTER JOIN cte_99_9999_s ON cte_training.rid = cte_99_9999_s.rid
LEFT OUTER JOIN cte_99_9999_e ON cte_training.rid = cte_99_9999_e.rid
LEFT OUTER JOIN cte_99_9999_d ON cte_training.rid = cte_99_9999_d.rid
LEFT OUTER JOIN cte_99_9999_c ON cte_training.rid = cte_99_9999_c.rid

ORDER BY cte_training.rid

我总结了sql,因为它可能会长达数千行。请注意,在表中,六位数代码(以XX-XXXX的形式)实际上是行中的数据。似乎可能是一个透视查询(或4)可能会使这更简洁,但我不知道它是否有助于或损害性能。

有更好的方法吗?有什么想法吗?

更新: 感谢任何人重新提出我的问题。我不知道如何描述这件事。

我已经探索过使用数据透视方法,它的运行速度提高了几个数量级。我最终把它分成4个不同的查询,但我认为这应该没问题。

我还从以前的开发人员那里发现,代码列表基本上是静态的,所以这有点帮助。

以下是我现在正在使用的内容:

SELECT rid, 
[11-9111] AS [11-9111], [11-9121] AS [11-9121], [19-1042] AS [19-1042], [21-1010] AS [21-1010], [21-1011] AS [21-1011], [21-1090] AS [21-1090], [21-1091] AS [21-1091], [21-1094] AS [21-1094], [21-1798] AS [21-1798], [27-3091] AS [27-3091], [29-1031] AS [29-1031], [29-1050] AS [29-1050], [29-1125] AS [29-1125], [29-1126] AS [29-1126], [29-1127] AS [29-1127], [29-1128] AS [29-1128], [29-1140] AS [29-1140], [29-1190] AS [29-1190], [29-1199] AS [29-1199], [29-2011] AS [29-2011], [29-2012] AS [29-2012], [29-2021] AS [29-2021], [29-2030] AS [29-2030], [29-2031] AS [29-2031], [29-2032] AS [29-2032], [29-2034] AS [29-2034], [29-2035] AS [29-2035], [29-2040] AS [29-2040], [29-2041] AS [29-2041], [29-2042] AS [29-2042], [29-2050] AS [29-2050], [29-2051] AS [29-2051], [29-2052] AS [29-2052], [29-2053] AS [29-2053], [29-2054] AS [29-2054], [29-2055] AS [29-2055], [29-2060] AS [29-2060], [29-2061] AS [29-2061], [29-2070] AS [29-2070], [29-2071] AS [29-2071], [29-2090] AS [29-2090], [29-2099] AS [29-2099], [31-1010] AS [31-1010], [31-1011] AS [31-1011], [31-1012] AS [31-1012], [31-1014] AS [31-1014], [31-1015] AS [31-1015], [31-1016] AS [31-1016], [31-2010] AS [31-2010], [31-2011] AS [31-2011], [31-2012] AS [31-2012], [31-2020] AS [31-2020], [31-2021] AS [31-2021], [31-2022] AS [31-2022], [31-9010] AS [31-9010], [31-9090] AS [31-9090], [31-9091] AS [31-9091], [31-9092] AS [31-9092], [31-9093] AS [31-9093], [31-9094] AS [31-9094], [31-9095] AS [31-9095], [31-9097] AS [31-9097], [31-9099] AS [31-9099], [31-9999] AS [31-9999], [43-4171] AS [43-4171], [43-5031] AS [43-5031], [43-6013] AS [43-6013], [51-9081] AS [51-9081], [99-9999] AS [99-9999]
FROM

(SELECT rid, training_program_soc, 
    CASE WHEN LEN(LTRIM(vocational_degree_received)) > 0 THEN 1 ELSE NULL END  AS vocational_degree_received 
FROM _training) vdr

PIVOT
(
    SUM(vocational_degree_received)
    FOR
    training_program_soc IN
    ( 
[11-9111], [11-9121], [19-1042], [21-1010], [21-1011], [21-1090], [21-1091], [21-1094], [21-1798], [27-3091], [29-1031], [29-1050], [29-1125], [29-1126], [29-1127], [29-1128], [29-1140], [29-1190], [29-1199], [29-2011], [29-2012], [29-2021], [29-2030], [29-2031], [29-2032], [29-2034], [29-2035], [29-2040], [29-2041], [29-2042], [29-2050], [29-2051], [29-2052], [29-2053], [29-2054], [29-2055], [29-2060], [29-2061], [29-2070], [29-2071], [29-2090], [29-2099], [31-1010], [31-1011], [31-1012], [31-1014], [31-1015], [31-1016], [31-2010], [31-2011], [31-2012], [31-2020], [31-2021], [31-2022], [31-9010], [31-9090], [31-9091], [31-9092], [31-9093], [31-9094], [31-9095], [31-9097], [31-9099], [31-9999], [43-4171], [43-5031], [43-6013], [51-9081], [99-9999])
) AS pvt
ORDER BY rid

我可能会重新调整列,以使它们更容易识别。我将在另外3个查询中进行不同的列测试,并可能加入它们。

2 个答案:

答案 0 :(得分:2)

这个可能会有所改善:

WITH cte_training AS (
  SELECT
    rid,
    Training_program_SOC,
    Started     = CASE WHEN ISDATE(vocational_training_begin_date) = 1                                           THEN 'Yes' ELSE 'No' END
    Completed   = CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_training_completed = 1       THEN 'Yes' ELSE 'No' END
    Degree      = CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_degree_received is not NULL  THEN 'Yes' ELSE 'No' END
    Certificate = CASE WHEN ISDATE(vocational_training_end_date) = 1 AND Vocational_license_received IS NOT NULL THEN 'Yes' ELSE 'No' END
  FROM _training
),
unpivoted AS (
  SELECT
    rid,
    Item = Training_program_SOC + ' ' + Item,
    Status
  FROM cte_training
  UNPIVOT (
    Status FOR Item IN (Started, Completed, Degree, Certificate)
  ) AS u
)

SELECT
  rid,

  [00-0000 Started], 
  [00-0000 Completed],
  [00-0000 Degree],
  [00-0000 Certificate],

  [11-9111 Started], 
  [11-9111 Completed],
  [11-9111 Degree],
  [11-9111 Certificate],

  ...

  [99-9999 Started], 
  [99-9999 Completed],
  [99-9999 Degree],
  [99-9999 Certificate]

FROM unpivoted
PIVOT (
  MAX(Status)
  FOR Item IN (
    [00-0000 Started], [00-0000 Completed], [00-0000 Degree], [00-0000 Certificate],
    [11-9111 Started], [11-9111 Completed], [11-9111 Degree], [11-9111 Certificate],
    ...
    [99-9999 Started], [99-9999 Completed], [99-9999 Degree], [99-9999 Certificate]
  )
) AS p
ORDER BY rid
;

基本上,第一个CTE根据与查询中相同的条件为每一行生成四个状态列。

第二个CTE 展开状态,并将状态名称与Training_program_SOC值合并,以便在最后阶段将它们用作列名。

主SELECT使用Item值作为列名称并将Status作为对应值,以第二CTE的结果为中心。两个长列列表,一个在SELECT子句中,另一个在PIVOT子句中,实际上是相同的,因此在将它放入动态查询之前,只需要在应用程序中构建一次列表。

答案 1 :(得分:1)

我会尝试使用这样的交叉表查询:

SELECT  rid,
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_begin_date) = 1 AND Training_program_SOC='00-0000') THEN 'Yes' END)), 'No') [00-0000 Started],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_training_completed = 1 AND Training_program_SOC='00-0000') THEN 'Yes' END)), 'No') [00-0000 Completed],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_degree_received IS NOT NULL AND Training_program_SOC='00-0000') THEN 'Yes' END)), 'No') [00-0000 Degree],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_license_received IS NOT NULL AND Training_program_SOC='00-0000') THEN 'Yes' END)), 'No') [00-0000 Certificate],

    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_begin_date) = 1 AND Training_program_SOC='11-9111') THEN 'Yes' END)), 'No') [11-9111 Started],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_training_completed = 1 AND Training_program_SOC='11-9111') THEN 'Yes' END)), 'No') [11-9111 Completed],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_degree_received IS NOT NULL AND Training_program_SOC='11-9111') THEN 'Yes' END)), 'No') [11-9111 Degree],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_license_received IS NOT NULL AND Training_program_SOC='11-9111') THEN 'Yes' END)), 'No') [11-9111 Certificate],
...
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_begin_date) = 1 AND Training_program_SOC='99-9999') THEN 'Yes' END)), 'No') [99-9999 Started],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_training_completed = 1 AND Training_program_SOC='99-9999') THEN 'Yes' END)), 'No') [99-9999 Completed],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_degree_received IS NOT NULL AND Training_program_SOC='99-9999') THEN 'Yes' END)), 'No') [99-9999 Degree],
    ISNULL(MAX(CASE WHEN ISDATE(vocational_training_end_date) = 1 AND vocational_license_received IS NOT NULL AND Training_program_SOC='99-9999') THEN 'Yes' END)), 'No') [99-9999 Certificate],

FROM    _training
GROUP BY rid
ORDER BY rid

我会让别人发布此版本的PIVOT版本