【问题标题】:How to simplify subqueries in cases with variables or other techniques - MS SQL如何在使用变量或其他技术的情况下简化子查询 - MS SQL
【发布时间】:2017-03-10 12:27:24
【问题描述】:

我仍在学习 SQL,我花了几个小时寻找我的问题的答案,但无济于事。如果这是一个骗局,请指出正确的方向!

我正在根据工作流工具生成的数据构建报告。这是我需要的结果:

| ID | Name | Company | Contract  |
| 1  | John | Acme    | Freelance |
| 2  | Bill | Acme    | Interne   |
| 3  | Mike | Duff    | Salaried  |

这里是主实例表“Instance_Table”:

| ID | Status | Date       |
| 1  | OK     | 2016-10-21 |
| 2  | Failed | 2016-10-24 |
| 3  | OK     | 2016-10-25 |

这是数据所在的“Data_Table”:

| DataID | InstanceID | VariableID | Value      |
| 1000   | 1          | 453        | John       |
| 1001   | 1          | 879        | Acme       |
| 1002   | 1          | 1879       | Freelance  |
| 1003   | 1          | 98         | NULL       |
| 1010   | 2          | 453        | Bill       |
| 1011   | 2          | 879        | Acme       |
| 1012   | 2          | 1879       | NULL       |
| 1013   | 2          | 98         | INT        |
| 1020   | 3          | 453        | Mike       |
| 1021   | 3          | 879        | Duff       |
| 1022   | 3          | 1879       | Salaried   |
| 1023   | 3          | 98         | SAL        |

如您所见,合约有 2 个可能的变量,1879 和 98。根据实例,仅填充一个或两个,并且 98 使用短代码而不是完整的单词。

这是我得到结果的痛苦方式:

SELECT IT.ID,

(SELECT DT.value FROM Data_Table DT
 WHERE DT.VariableID = 453 AND IT.ID = DT.InstanceID) as "Name",

(SELECT DT.value FROM Data_Table DT
 WHERE DT.VariableID = 879 AND IT.ID = DT.InstanceID) as "Company",

CASE
 WHEN (SELECT DT.value FROM Data_Table DT
       WHERE DT.VariableID = 1897 AND IT.ID = DT.InstanceID) is NOT NULL
  THEN (SELECT DT.value FROM Data_Table DT
       WHERE DT.VariableID = 1897 AND IT.ID = DT.InstanceID)
  ELSE CASE
        WHEN (SELECT DT.value FROM Data_Table DT
              WHERE DT.VariableID = 98 AND IT.ID = DT.InstanceID) = 'SAL'
         THEN 'Salaried'
        WHEN (SELECT DT.value FROM Data_Table DT
              WHERE DT.VariableID = 98 AND IT.ID = DT.InstanceID) = 'INT'
         THEN 'Intern'
        WHEN (SELECT DT.value FROM Data_Table DT
              WHERE DT.VariableID = 98 AND IT.ID = DT.InstanceID) = 'FRE'
         THEN 'Freelance'
        ELSE '-'
      END
 END as "Contract"

FROM Instance_Table IT

我在问如何使用变量或其他方法简化每个子查询的重复,因为我可以想象多次查询同一个表会对性能造成巨大影响。

这个例子当然是我实际所做的一个非常简化的版本......我有大约 30 列要放入我的报告中,数据以各种形式出现,有近 70 个子查询实例,其中很多被重复。该查询的运行时间很长,如果能帮助它更快地运行,我们将不胜感激。

【问题讨论】:

  • "如您所见,合约有 2 个可能的变量,1879 和 98。"其实我完全看不出来。我根本无法遵循这一点。您能否改写一下以更清楚地说明您的意思?你在说什么?我真的看不到两个表之间有任何数据关系,可以让您确定性地得出您想要的结果。您没有向我们提供所有必要的信息。
  • 你能修复你的数据结构吗?拥有这样的数据表是您永远无法轻松或以高性能方式查询的东西。如果您确实无法定义您想要的实际字段(根据我的经验,只有不到 1% 的时间是正确的),那么请使用针对此类非关系查询进行了优化的 nosql 数据库。
  • @pmbAustin 我认为这很清楚,抱歉。如果您查看 Data_Table 中的每个 InstanceID,您会看到变量 1897 和 98 发生了变化(有时为 NULL,有时是代码,有时是合约的全名)。然而,变量 483 和 879 总是以相同的方式给出常数数据。但是,我绝对可以通过我所做的查询得到我想要的日期。
  • @HLGEM 不,我无法控制数据结构。不幸的是,工作流系统是开箱即用的。
  • 我仍然不知道您应用什么“逻辑”来获取表 2 和 3 并生成表 1。它看起来完全是任意的,没有您确定的规则,也没有我能做的规则想出这样一个翻译。您的问题仍然完全不清楚,甚至没有足够的信息可供任何人帮助您。

标签: sql sql-server sql-server-2012 subquery case


【解决方案1】:

大概是这样的吧?

SELECT IT.id,
       DT_Name.value AS NAME,
       DT_Cmpy.value AS Company,
       [Contract]=CASE
                    WHEN DT_Cont_1.value IS NOT NULL THEN DT_Cont_1.value
                    ELSE
                      CASE DT_Cont_2.value
                        WHEN 'SAL' THEN 'Salaried'
                        WHEN 'INT' THEN 'Intern'
                        WHEN 'FRE' THEN 'Freelance'
                        ELSE '-'
                      END
                  END
FROM   instance_table IT
       LEFT JOIN data_table DT_Name
              ON DT_Name.variableid = 453
                 AND IT.id = DT_Name.instanceid
       LEFT JOIN data_table DT_Cmpy
              ON DT_Cmpy.variableid = 879
                 AND IT.id = DT_Cmpy.instanceid
       LEFT JOIN data_table DT_Cont_1
              ON DT_Cont_1.variableid = 1897
                 AND IT.id = DT_Cont_1.instanceid
       LEFT JOIN data_table DT_Cont_2
              ON DT_Cont_2.variableid = 98
                 AND IT.id = DT_Cont_2.instanceid  

【讨论】:

  • 你也可以使用两个连接和一个分组——如果他有 30 个以上的列,在他的情况下可能会更好。
  • COALESCE 也可能比外部的 CASE 语句更好。
  • @Hogan Myeah 使用 COALESCE 会更短一些,但会扩展到相同的结构。 SQL Server 上的 ISNULL 通常是首选。
  • 一些系统被优化为 COALESCE over CASE(DB2 是我见过的)。
  • 伟大的 cmets,谢谢。我会试试这些并告诉你。
【解决方案2】:

使用LEFT JOIN 代替相关子查询

SELECT IT.ID,
       DT1.value                AS "Name",
       DT2.value                AS "Company",
       COALESCE(DT3.value, CASE DT4.value
                             WHEN 'SAL' THEN 'Salaried'
                             WHEN 'INT' THEN 'Intern'
                             WHEN 'FRE' THEN 'Freelance'
                             ELSE '-'
                           END) AS "Contract"
FROM   Instance_Table IT
       LEFT JOIN Data_Table DT1
              ON IT.ID = DT1.InstanceID
                 AND DT1.VariableID = 453
       LEFT JOIN Data_Table DT2
              ON IT.ID = DT2.InstanceID
                 AND DT2.VariableID = 879
       LEFT JOIN Data_Table DT3
              ON IT.ID = DT3.InstanceID
                 AND DT3.VariableID = 1897
       LEFT JOIN Data_Table DT4
              ON IT.ID = DT4.InstanceID
                 AND DT4.VariableID = 98
                 AND DT.value IN ( 'SAL', 'INT', 'FRE' ) 

【讨论】:

  • 谢谢,我需要更多地研究 COALESCE 才能更好地理解它。在这种情况下,我想我了解 LEFT JOIN 主体,我需要为每个唯一变量号创建一个连接实例。一种或另一种方式,这是一个复杂的查询。我会尽力让你知道。
【解决方案3】:

您可以在不多次连接 data_table 的情况下执行此操作。

SELECT 
    it.ID,
    MAX(CASE WHEN DT.VariableID = 453 THEN DT.VALUE END) AS "Name",
    MAX(CASE WHEN DT.VariableID = 879 THEN DT.VALUE END) AS "Company",
    COALESCE(MAX(CASE WHEN DT.VariableID = 1897 THEN DT.VALUE END), 
        MAX(CASE WHEN DT.VariableID = 98 AND dt.VALUE = 'SAL' THEN 'Salary' 
                 WHEN DT.VariableID = 98 AND dt.VALUE = 'INT' THEN 'Intern'
                 WHEN DT.VariableID = 98 AND dt.VALUE = 'FRE' THEN 'Freelance'
                END)
    ) AS "Contract"
FROM 
    Instance_Table it
    LEFT JOIN Data_Table dt ON  it.ID = dt.InstanceID
GROUP BY it.ID

【讨论】:

  • 鉴于 GROUP BY,我认为您的 Contract 逻辑不正确
  • 我认为只有当 VariableID 1879 的值与 98 的值不同时才会出现问题。如果它们都匹配,例如 Salary, SAL,那么它们应该返回正确的 @ 值987654323@。如果它们可以不同并且 VariableID 1879 取代 98 那么我在编辑之前的第一个答案应该可以工作
  • 不幸的是,我无法控制索引和键。而且由于我无法控制变量 98 和 1879 中的数据,我不知道这将如何提供恒定的结果。如果以下解决方案不起作用,我会调查它。谢谢。
  • @Hogan 我想我错过了你的观点。如果 VariableID 为 1879 并且 Variable 为 null,那么 MAX 聚合将忽略它并获得 VariableID = 98 的 MAX 值。它应该如何工作?
  • @Hogan 编辑只是强制它使用分配给 VariableID 1897 的任何值,如果 Value IS NOT NULL.. 如果 VariableID 98 与 1897 不同.. 其他方式总是 pic Salaried如果 ID 存在,则不管 VariableID 1897 的非空值如何,都超过其他值。这只会改变优先级。如果VariableID is 1879 and Variable is null 这个编辑没有改变任何东西
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-01-12
  • 2015-10-03
  • 2021-05-29
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多