查找 T-SQL 以返回这些值

Posted

技术标签:

【中文标题】查找 T-SQL 以返回这些值【英文标题】:Finding the T-SQL to return these values 【发布时间】:2009-02-25 15:55:08 【问题描述】:

我正在尝试编写一个存储过程,它将根据以下规则为每条记录返回两个计算值,但我还没有弄清楚如何构建 SQL 来实现它。我正在使用 SQL Server 2008。

首先,相关的表格和与问题相关的字段。

生产运行

 RunID (key, and RunID is given to the stored proc as its parameter)
 ContainerName
 ProductName
 TemplateID

模板测量

 MeasurementTypeID
 TemplateID

简单边界

 MeasurementTypeID
 TemplateID
 UpperBound
 LowerBound

容器边界

 MeasurementTypeID
 TemplateID
 UpperBound
 LowerBound
 ContainerName

产品边界

 MeasurementTypeID
 TemplateID
 UpperBound
 LowerBound
 ProductName

这就是我想要返回的。我想为每个 TemplateMeasurements 记录返回一个计算的上限和下限值,该记录与具有提供的 runID 的 ProductionRuns 记录匹配。

计算的上限和下限基本上得到了作为简单、容器和产品边界的结果可以获得的最严格的边界,如果它们符合条件的话。

如果存在具有正确 MeasurementTypeID 和 TemplateID 的 SimpleBounds 记录,则该记录将成为特定 MeasurementTypeID 和 TemplateMeasurements 记录的限定范围之一。

要使 ContainerBound 记录符合条件,TemplateID 和 MeasurementTypeID 必须匹配,而且 ContainerName 必须与 ProductionRuns 记录中的 ContainerName 值匹配。对于 ProductBounds 也是如此,但对于 ProductName。

对于特定的MeasurementTypeID,获取所有限定范围,并找到最小的Upper Bound,这将是要返回的计算的Upper Bound。找到限定符的最大下界,这将是返回的下界。

不过,我不知道如何组合 SQL 来执行此操作。

此外,如果三个绑定表都不符合特定 MeasurementTypeID 的条件,则可能返回 null。

我的想法是某种左外连接,但我不确定如何将其扩展到三个结果中可能都为 null 的表。

感谢您的帮助。

【问题讨论】:

【参考方案1】:

我现在没有时间测试这个,但希望这会让你非常接近:

SELECT
     PR.RunID,
     PR.TemplateID,
     CASE
          WHEN MAX(SB.LowerBound) > MAX(CB.LowerBound) AND
                      MAX(SB.LowerBound) > MAX(PB.LowerBound) THEN MAX(SB.LowerBound)
          WHEN MAX(CB.LowerBound) > MAX(PB.LowerBound) THEN MAX(CB.LowerBound)
          ELSE MAX(PB.LowerBound)
     END AS LowerBound,
     CASE
          WHEN MIN(SB.UpperBound) < MIN(CB. UpperBound) AND
                      MIN(SB. UpperBound) < MIN(PB. UpperBound) THEN MIN(SB. UpperBound)
          WHEN MIN(CB. UpperBound) < MIN(PB. UpperBound) THEN MIN(CB. UpperBound)
          ELSE MIN(PB. UpperBound)
     END
FROM
     ProductionRuns PR
INNER JOIN TemplateMeasurements TM ON
      TM.TemplateID = PR.TemplateID
LEFT OUTER JOIN SimpleBounds SB ON
     SB.TemplateID = PR.TemplateID AND
     SB.MeasurementTypeID = TM.MeasurementTypeID
LEFT OUTER JOIN ContainerBounds CB ON
     CB.TemplateID = PR.TemplateID AND
     CB.MeasurementTypeID = TM.MeasurementTypeID AND
     CB.ContainerName = PR.ContainerName
LEFT OUTER JOIN ProductBounds PB ON
     PB.TemplateID = PR.TemplateID AND
     PB.MeasurementTypeID = TM.MeasurementTypeID AND
     PB.ProductName = PR.ProductName
GROUP BY
     PR.RunID,
     PR.TemplateID

【讨论】:

感谢您的代码。我会看看它。 ProductBounds 需要使用 PB.ProductName = PR.ProductName 但除此之外它看起来很棒!【参考方案2】:

不要从 Tom H. 的回答中拿走,但您也可以考虑使用联合而不是连接来解决这个问题,以帮助拆分不同的上/下规则。这取决于您认为查询将来需要如何更改(如果有的话)。

查询最终看起来更干净,尤其是在没有所有 CASE 规则的情况下,但在 TemplateMeasurement 行不存在的情况下它可能没有那么有用。

SELECT RunID, TemplateID, MIN(UpperBound), MAX(LowerBound)
FROM

  (SELECT PR.RunID, SB.TemplateID, SB.UpperBound, SB.LowerBound
  FROM SimpleBounds SB
  INNER JOIN TemplateMeasurements TM
      ON  SB.TemplateID = TM.TemplateID
      AND SB.MeasurementTypeID = TM.MeasurementTypeID
  INNER JOIN ProductionRuns PR
      ON  TM.TemplateID = PR.TemplateID)

UNION

  (SELECT PR.RunID, CB.TemplateID, CB.UpperBound, CB.LowerBound
  FROM ContainerBounds CB
  INNER JOIN TemplateMeasurements TM
      ON  CB.TemplateID = TM.TemplateID
      AND CB.MeasurementTypeID = TM.MeasurementTypeID
  INNER JOIN ProductionRuns PR
      ON  TM.TemplateID = PR.TemplateID
      AND CB.ContainerName = PR.ContainerName)

UNION

  (SELECT PR.RunID, PB.TemplateID, PB.UpperBound, PB.LowerBound
  FROM ProductBounds PB
  INNER JOIN TemplateMeasurements TM
      ON  PB.TemplateID = TM.TemplateID
      AND PB.MeasurementTypeID = TM.MeasurementTypeID
  INNER JOIN ProductionRuns PR
      ON  TM.TemplateID = PR.TemplateID
      AND PB.ProductName = PR.ProductName)

GROUP BY RunID, TemplateID

【讨论】:

我以前从未使用过联合体。我将不得不看看他们是如何工作的。感谢您的代码。【参考方案3】:

您已经得到了其他应该可以使用的答案,但在我看来,这种类型的 UNIONed 内部查询可以以最简洁、最易于维护的方式将水平层次结构折叠成垂直层次结构,这基本上是您的问题:

SELECT MIN(iq.upperbound), MAX(iq.lowerbound)
FROM TemplateMeasurements tm
    INNER JOIN ProductionRuns pr ON tm.TemplateID = pr.TemplateID
    LEFT JOIN
    (
    SELECT sb.UpperBound, sb.LowerBound, sb.MeasurementTypeID, '' as Name, 'sb' as Type, sb.TemplateID
    FROM SimpleBounds sb 
    UNION ALL
    SELECT cb.UpperBound, cb.LowerBound, cb.MeasurementTypeID, cb.ContainerName as Name, 'cb' as Type, cb.TemplateID
    FROM ContainerBounds cb 
    UNION ALL
    SELECT pb.UpperBound, pb.LowerBound, pb.MeasurementTypeID, pb.ProductName as Name, 'pb' as Type, pb.TemplateID
    FROM ProductBounds pb 
    ) iq ON iq.MeasurementTypeID = tm.MeasurementTypeID 
        AND iq.TemplateID = tm.TemplateID 
        AND iq.Name = 
            CASE iq.Type 
             WHEN 'sb' THEN iq.Name 
             WHEN 'cb' THEN pr.ContainerName 
             WHEN 'pb' THEN pr.ProductName 
            END
    WHERE pr.RunID = @runid
    GROUP BY tm.TemplateID, tm.MeasurementTypeID

【讨论】:

【参考方案4】:

感谢您引导我朝着正确的方向前进。在我把它调整得恰到好处之前,我不得不摆弄这个问题一段时间,但现在效果很好。

我的最终代码和结果:

 ALTER PROCEDURE [dbo].[GetBounds]
 @runID int
 AS
 BEGIN
    SET NOCOUNT ON;
    DECLARE @templateID int
    SET @templateID = (SELECT TOP(1) TemplateID 
    FROM ProductionRuns WHERE RunID = @runID);

    SELECT TM.MeasurementTypeID,

    CASE 
    WHEN MIN(SB.UpperBound) < MIN(PB.UpperBound) 
    AND MIN(SB.UpperBound) < MIN(CB.UpperBound) THEN MIN(SB.UpperBound)
    WHEN MIN(PB.UpperBound) < MIN(SB.UpperBound) 
    AND MIN(PB.UpperBound) < MIN(CB.UpperBound) THEN MIN(PB.UpperBound)
    WHEN MIN(CB.UpperBound) < MIN(SB.UpperBound) 
    AND MIN(CB.UpperBound) < MIN(PB.UpperBound) THEN MIN(CB.UpperBound)
    ELSE MIN(SB.UpperBound) 
    END AS 'UpperBound',

    CASE
    WHEN MAX(SB.LowerBound) > MAX(PB.LowerBound) 
    AND MAX(SB.LowerBound) > MAX(CB.LowerBound) THEN MAX(SB.LowerBound)
    WHEN MAX(PB.LowerBound) > MAX(SB.LowerBound) 
    AND MAX(PB.LowerBound) > MAX(CB.LowerBound) THEN MAX(PB.LowerBound)
    WHEN MAX(CB.LowerBound) > MAX(SB.LowerBound) 
    AND MAX(CB.LowerBound) > MAX(PB.LowerBound) THEN MAX(CB.LowerBound)
    ELSE MAX(SB.LowerBound)
    END AS 'LowerBound'

    FROM
    ProductionRuns PR
    INNER JOIN TemplateMeasurements TM ON
    TM.TemplateID = PR.TemplateID
    LEFT OUTER JOIN SimpleBounds SB ON
    SB.TemplateID = PR.TemplateID AND
    SB.MeasurementTypeID = TM.MeasurementTypeID
    LEFT OUTER JOIN ContainerBounds CB ON
    CB.TemplateID = PR.TemplateID AND
    CB.MeasurementTypeID = TM.MeasurementTypeID AND
    CB.ContainerName = PR.ContainerName
    LEFT OUTER JOIN ProductBounds PB ON
    PB.TemplateID = PR.TemplateID AND
    PB.MeasurementTypeID = TM.MeasurementTypeID AND
    PB.ProductName = PR.ProductName 

    WHERE TM.TemplateID = @templateID

    GROUP BY
    TM.MeasurementTypeID
 END

特定案例的部分结果,RunID = 3249 (TemplateID = 2)

MeasurementTypeID   UpperBound  LowerBound
2   NULL    NULL
11  4   2.5
18  30  1
20  40  10
33  99  0
36  200 140
42  120 32
...

【讨论】:

以上是关于查找 T-SQL 以返回这些值的主要内容,如果未能解决你的问题,请参考以下文章

t-SQL 查找每个组的前 10 条记录

Excel - 在表中查找值以返回第 n 列值

t-sql 用户定义函数,用表中的查找替换文本

用于查找冗余索引的 T-SQL

查找并返回非重复值

遍历字典数组以查找值