【问题标题】:Make SQL function run for every value in table为表中的每个值运行 SQL 函数
【发布时间】:2015-02-17 20:43:44
【问题描述】:

这可能是一个新手问题,但我终其一生都无法弄清楚。

在解释我的存储过程之前,我将描述我的表并展示示例。有一个TicketBook 表,其中包含已签发给一个单元的所有票本。 TicketBooks 有 25 张票长。

例如:

                        TicketBooks
                        -----------
TicketBookNum  |  TicketNum  |  UnitID  |  FirstTicket  |  LastTicket | Used
 ---------------------------------------------------------------------------
     101              101          120           101           126       Yes
     101              102          120           101           126       Yes
     101              103          120           101           126       No
     101              104          120           101           126       Yes
     etc...

此表包含数据库中已使用和未使用的所有票证。

不同的票本可以同时发给不同的单位。所以 TicketBook 101 可以发给 Unit 120,而 TicketBook 151 可以发给 140。

我的存储过程需要做的是返回所有标记为未使用的票证,但也有一个大于标记为已使用的票证。

例子:

TicketBookNum  |  TicketNum | Used
----------------------------------
   101              101        Yes
   101              102        Yes
   101              103        No
   101              104        Yes
   101              105        Yes
   101              106        No
   101              107        No
   etc..

因此,存储过程返回票证103,但不返回106107,因为在票簿内没有票证在它们之后被标记为已使用。

这是我目前使用的存储过程:

select TicketNum
from TicketBooks
where Used='No'
and TicketNum between 92226 and 92251
and TicketNum < (select top 1 TicketNum
                from TicketBooks
                where Used='Yes'
                and TicketNum between 92226 and 92251
                order by TicketNum desc)
order by TicketNum desc

这将从 TicketBook 82226 返回丢失的票。如果我尝试更改 between 子句中的参数,则它不会返回正确的结果。任何帮助将不胜感激。

【问题讨论】:

  • 只是一个不能解决您问题的评论:您应该只使用max() 而不是top 1order by

标签: sql sql-server missing-data


【解决方案1】:

您可以使用公用表表达式来包装范围限制并执行这样的存在查询:

;with cte as (
    select * from TicketBooks where TicketNum between 101 and 126
    )

select * from cte where Used = 'no'
and exists (
    select 1 
    from cte t
    where t.TicketNum > cte.ticketnum
    and t.used='yes'
    )

这仍然限制在硬编码范围内;更好的选择可能是根据源表中的 FirstTicket/LastTicket 值构建范围。

【讨论】:

    【解决方案2】:

    我相信我理解你想要做什么。我认为您缺少的是您的子查询未映射到外部查询中的票簿。所以你想要这样的东西:

    select TicketNum
    from TicketBooks a
    where Used='No'
    and TicketNum < (select MAX(b.TicketNum)
                from TicketBooks b
                where b.Used='Yes'
                and b.TicketBookNum = a.TicketBookNum)
    order by TicketNum desc
    

    【讨论】:

    • 这很完美,你在做什么也很有意义。非常感谢!!
    【解决方案3】:

    试试这个

     ;WITH cte AS
    (
     select TicketNum,ROW_NUMBER() OVER
        (PARTITION BY TicketBookNum,Used ORDER BY TicketNum) rn
    from TicketBooks
    
    )
    SELECT * FROM cte 
    where Used='No' AND rn =1 
    

    例子

    CREATE TABLE #t (ticket INT ,ticketno INT, fla VARCHAR(10))
    
    INSERT INTO #t VALUES(1,1,'yes'),
    (1,2,'yes'),
    (1,4,'No'),
    (1,5,'No')
    
    ;WITH cte AS
    (
    
    SELECT *,ROW_NUMBER() OVER(PARTITION BY ticket,fla ORDER BY ticketno) rn FROM #t 
     )
    
     SELECT * FROM cte WHERE fla ='no'AND rn =1 
    

    【讨论】:

    • 我试了一下。它不适用于where Used='No',但如果我把它拿出来,我会从每张票簿中得到第一张未使用的票。这几乎就是我想要的。我正在该票簿中查找最后一张之前未使用的票。
    【解决方案4】:
    select TicketNum
    from TicketBooks as t
    where Used='No'
        and TicketNum between 92226 and 92251
        and exists (
            select 1
            from TicketBooks as t2
            where t2.Used='Yes'
                /* not sure if you actually want to restrict the range here */
                and t2.TicketNum between 92226 and 92251
                and t2.TicketNum > t.TicketNum
        )
    order by TicketNum desc
    

    我不知道您是想在票务簿或明确范围内搜索还是什么。但我写这篇文章是为了向您展示EXISTS 选项。您还可能会发现 &lt; ANY (...)&lt; ALL (...) 在将来很有用。许多人忽略了这些。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-03
      • 1970-01-01
      • 2018-06-30
      • 2017-04-08
      相关资源
      最近更新 更多