【问题标题】:How to NOT loop in SQL working with sets如何在使用集合的 SQL 中不循环
【发布时间】:2013-10-18 15:02:41
【问题描述】:

我带着大量的命令式经验来到 SQL,并试图弄清楚如何在 SQL 中执行不需要循环的感觉。即,在您绝对会在命令式语言中循环的情况下,不循环 SQL 的惯用方法是什么?

我在 SQL (Server) 中有一个类似这样的表(假设我们选择标签 id 为 999 的位置,并且 inc_indx 是自动递增的)

inc_indx | State | Tag_Id
     400       5      999
     399       3      "
     397       0      "
     395      50      "
     392      39      "
...etc

基本上,状态会增加一段时间,然后意外地为零。我想获取给定集合中每个零值之前的最后一个值。

要这样做一次,我会这样做:

SELECT TOP 1 [State]
FROM [mytable]
WHERE [inc_indx] <
    (SELECT TOP 1 [inc_ndx]
    FROM [mytable]
    WHERE Tag_Id = 999
    AND State = 0)
AND Tag_Id = 999

要重复执行此操作,在命令式 pythonish 伪代码中并假设某种类型的 DB 连接处理类,我会这样做:

setOfZeroes = handler.query("SELECT [inc_ndx]
    FROM [mytable]
    WHERE Tag_Id = 999
    AND State = 0")

setOfMaxes = []
for index in setOfZeroes:
    max = handler.query("SELECT TOP 1 [State]
                  FROM [mytable]
                  WHERE [inc_indx] < "
                  + index + 
                  " AND Tag_Id = 999")
    setOfMaxes.add(max)

但是,假设我想在 SQL 中完成这一切并且我不想循环,那么 SQLese 的方法是什么?

回答:

简短版:CTE 和联接

长版(其中一些可能是不必要的):

-- narrow the scope, add a numbered row
WITH numbered AS (SELECT [State]
            ,inc_indx
            ,ROW_NUMBER() OVER (Order by inc_indx) As [row]
        FROM mytable
        WHERE Tag_Id = 999)
-- put the row numbers where state is zero in a second CTE
, zeroes AS (SELECT [row]
        FROM numbered
        WHERE State = 0)
SELECT [State], [inc_indx]
FROM numbered, zeroes
WHERE numbered.[row] = zeroes.[row] - 1
ORDER BY [inc_indx] desc

【问题讨论】:

    标签: sql iteration idioms


    【解决方案1】:

    我通常用 CTE 来分解问题:s

    WITH FirstZeroIndex AS (
        SELECT Tag_Id
              ,MIN(inc_indx) AS ZeroIdx
        FROM mytable
        WHERE State = 0
        GROUP BY Tag_Id
    )
    ,LastNonZeroIndex AS (
        SELECT mytable.Tag_Id
              ,MAX(mytable.inc_indx) AS NonZeroIdx
        FROM mytable
             LEFT JOIN FirstZeroIndex
                 ON myTable.Tag_Id = FirstZeroIndex.Tag_Id
        WHERE FirstZeroIndex.ZeroIdx IS NULL -- If we want tags without zeros
              OR mytable.inc_indx < FirstZeroIndex.ZeroIdx
        GROUP BY mytable.Tag_Id
    )
    SELECT * 
    FROM LastNonZeroIndex
    WHERE Tag_id = 999
    

    【讨论】:

    • 不完全是答案,但这让我到了那里。
    【解决方案2】:

    使用apply怎么样?毫无疑问,还有其他几种方法,但这种方法有效

    select q1.*
    from mytable as q1
    cross apply (
        select top 1 *
        from mytable as q2
        where q2.Tag_Id = q1.Tag_Id
            and q2.State = 0
            and q2.inc_indx > q1.inc_indx
        order by q2.inc_indx desc
    ) as q2
    

    这是我使用的架构/数据

    CREATE TABLE mytable (
        inc_indx int,
        State int,
        Tag_Id int
    )
    
    insert into mytable
    values
        (1, 5, 999), 
        (2, 3, 998), 
        (3, 0, 999), 
        (4, 50, 991), 
        (5, 39, 989), 
        (6, 0, 991)
    

    编辑:

    好吧,根据我从您的示例和伪代码中了解到的情况,一个标签可以有多个状态(包括 0),并且您想要特定标签的紧接在 0 状态之前的状态? Like so?

    【讨论】:

    • 这似乎得到了最后一个零之前的所有内容,我试图在每个 0 之前获取最后一个值。
    • 最后一个值是什么意思?你要哪一栏?除了SELECT *,你能不能只选择你想要的列?
    • 此生成生成“上次状态 = 0 之前的所有行”。我想要“State = 0 之前的每一行,每个 State = 0 在初始集中”。
    猜你喜欢
    • 2019-11-18
    • 2019-10-11
    • 1970-01-01
    • 1970-01-01
    • 2020-02-14
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 2013-03-24
    相关资源
    最近更新 更多