【问题标题】:Smallest available integer in subset of table - SQL表子集中的最小可用整数 - SQL
【发布时间】:2015-07-06 19:27:04
【问题描述】:

我正在使用存储开始日期、结束日期和用于排序的整数的现有表。

对于给定的开始和结束日期,我需要能够确定具有重叠日期范围的条目的最小可用整数。

例如,我的表可能会存储这些记录:

7 月 8 日 -> 7 月 9 日,排序指数为 0。

7 月 9 日 -> 7 月 10 日,排序指数为 1。

7 月 9 日 -> 7 月 11 日,排序指数为 2。

然后,给定日期范围 7 月 10 日 -> 7 月 11 日,我想将排序索引设置为 0

它需要在输入日期范围内没有其他条目的情况下工作(因此它可以默认为 0)。日期范围并不总是相隔两个日期,并且排序索引没有限制。

这是我所拥有的,它只返回最大订单索引之上的一个:

SELECT ISNULL(MAX(order_index),-1) + 1 FROM table
WHERE start_date <= @end AND end_date >= @start)

我尝试使用this answer,但无法获得预期的结果。

【问题讨论】:

  • 为什么 7 月 10 日至 11 日会为零?
  • 你在找最小的整数,为什么用MAX
  • @TabAlleman 这将是 0,因为在 7 月 10 日至 11 日之间,订单索引 1 和 2 已被使用,但 0 可用,因为 7 月 8 日至 9 日的记录不在范围内。

标签: sql sql-server


【解决方案1】:

这样的事情怎么样?

declare @SampleData table ([BeginDate] date, [EndDate] date, [Order] int);
insert @SampleData values
    ('2015-07-08', '2015-07-09', 0),
    ('2015-07-09', '2015-07-10', 1),
    ('2015-07-09', '2015-07-11', 2);

declare @Start date = '2015-07-10';
declare @End date = '2015-07-11';

with [OrderingCTE] as
(
    select
        [Order],
        [Ideal Order] = row_number() over (order by [Order]) - 1
    from
        @SampleData
    where
        [BeginDate] <= @End and
        [EndDate] >= @Start
)
select coalesce
(
    min(case [Order] when [Ideal Order] then null else [Ideal Order] end),
    max([Order]) + 1
)
from
    [OrderingCTE];

CTE 为源表中的每条记录生成两个排序:[Order] 是存储在记录中的实际值,[Ideal Order] 是如果所有可能的排序(从零开始)在给定的日期范围内使用。

如果在任何时候[Ideal Order][Order] 不同,您可以推断当前[Ideal Order] 值尚未使用,因此是最小可用值。如果这在任何时候都不是真的,那么最小可用值比迄今为止使用的最大值大一;这是脚本底部COALESCE 的后半部分。

最后一点:您链接的问题 another answer 引起了对可能出现的竞争条件的担忧,具体取决于您尝试如何使用以这种方式查询的数据。如果您还没有这样做,我强烈建议您看看它。

【讨论】:

  • 如果在标量值函数中使用它,我如何返回结果?
  • 您要做的就是声明一个标量变量(例如@Result),将select coalesce 的行更改为select @Result = coalesce,然后再更改为return @Result。查询的逻辑根本没有改变。有关示例,请参阅Types of Functions
  • 美丽。如果最后为 null,我必须返回 0,否则就成功了。
【解决方案2】:

我会尝试这样的:

SELECT
  COALESCE(
    MIN(CASE WHEN t2.order_index IS NULL THEN t1.order_index - 1 ELSE NULL END),
    MAX(t1.order_index) + 1,
    0)
FROM TheTable t1
LEFT JOIN TheTable t2
  ON t2.order_index = t1.order_index - 1
    AND t2.start_date <= @end
    AND t2.end_date >= @start
WHERE t1.start_date <= @end
  AND t1.end_date >= @start

【讨论】:

    【解决方案3】:

    我认为您可以通过枚举值来做到这一点。如果我假设订单索引不重复,那么您可以使用row_number() 和一些算术来找到“漏洞”。处理边缘情况需要额外的逻辑。

    with t as (
          select t.*,
                 row_number() over (order by order_index) as seqnum,
                 min(order_index) over () as minoi,
                 max(order_index) over () as maxoi
          from table t
          where start_date <= @end and end_date >= @start
         )
    select (case when min(minoi) > 0 then 0
                 when min(minoi) is null then min(maxoi + 1)
                 else min(minoi + seqnum - 1)
            end)
    from t 
    where order_index <> minoi + seqnum - 1 or
          order_index = maxoi
    

    【讨论】:

    • 看起来很有希望,最终出现此错误:列 't.maxoi' 在选择列表中无效,因为它既不包含在聚合函数或 GROUP BY 子句中。
    • ^然后将[t.maxoi] 添加到您的GROUP BY 子句中
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-10
    • 1970-01-01
    • 2022-11-13
    • 1970-01-01
    • 2021-09-25
    • 2013-06-04
    • 1970-01-01
    相关资源
    最近更新 更多