Работа с периодами и датами без использования курсоров - PullRequest
1 голос
/ 22 октября 2010

Я хотел бы решить эту проблему, избегая использования курсоров (FETCH).

Здесь возникает проблема ...

1st Table/quantity
------------------
periodid periodstart periodend quantity

1        2010/10/01 2010/10/15    5


2st Table/sold items
-----------------------
periodid periodstart periodend solditems

14343    2010/10/05 2010/10/06    2

Теперь я хотел бы получить следующий вид илипросто результат запроса

Table Table/stock
-----------------------
periodstart periodend itemsinstock

2010/10/01 2010/10/04      5

2010/10/05 2010/10/06      3

2010/10/07 2010/10/15      5

Кажется невозможным решить эту проблему без использования курсоров или без использования отдельных дат вместо периодов.

Буду признателен за любую помощь.

Спасибо

Ответы [ 5 ]

2 голосов
/ 22 октября 2010
DECLARE @t1 TABLE (periodid INT,periodstart DATE,periodend DATE,quantity INT)
DECLARE @t2 TABLE (periodid INT,periodstart DATE,periodend DATE,solditems INT)

INSERT INTO @t1 VALUES(1,'2010-10-01T00:00:00.000','2010-10-15T00:00:00.000',5)
INSERT INTO @t2 VALUES(14343,'2010-10-05T00:00:00.000','2010-10-06T00:00:00.000',2)

DECLARE @D1 DATE

SELECT @D1 = MIN(P) FROM (SELECT MIN(periodstart) P FROM @t1
                          UNION ALL
                          SELECT MIN(periodstart) FROM @t2) D

DECLARE @D2 DATE

SELECT @D2 = MAX(P) FROM (SELECT MAX(periodend) P FROM @t1
                          UNION ALL
                          SELECT MAX(periodend) FROM @t2) D

;WITH 
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L4),
Dates AS(SELECT DATEADD(DAY,i-1,@D1) AS D FROM Nums where i <= 1+DATEDIFF(DAY,@D1,@D2))  , 
Stock As (
SELECT D ,t1.quantity - ISNULL(t2.solditems,0) AS itemsinstock
FROM Dates
LEFT OUTER JOIN @t1 t1 ON t1.periodend >= D and t1.periodstart <= D
LEFT OUTER JOIN @t2 t2 ON t2.periodend >= D and t2.periodstart <= D ),
NStock As (
select D,itemsinstock, ROW_NUMBER() over (order by D) - ROW_NUMBER() over (partition by itemsinstock order by D) AS G
from Stock)
SELECT MIN(D) AS periodstart, MAX(D) AS periodend, itemsinstock 
FROM NStock
GROUP BY G, itemsinstock
ORDER BY periodstart
1 голос
/ 22 октября 2010

Надеюсь, немного легче читать, чем у Мартина. Я использовал разные таблицы и примеры данных, надеюсь, экстраполируя правильную информацию:

CREATE TABLE [dbo].[Quantity](
    [PeriodStart] [date] NOT NULL,
    [PeriodEnd] [date] NOT NULL,
    [Quantity] [int] NOT NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[SoldItems](
    [PeriodStart] [date] NOT NULL,
    [PeriodEnd] [date] NOT NULL,
    [SoldItems] [int] NOT NULL
) ON [PRIMARY]

INSERT INTO Quantity (PeriodStart,PeriodEnd,Quantity)
SELECT '20100101','20100115',5

INSERT INTO SoldItems (PeriodStart,PeriodEnd,SoldItems)
SELECT '20100105','20100107',2 union all
SELECT '20100106','20100108',1

Фактический запрос сейчас:

;WITH Dates as (
    select PeriodStart as DateVal from SoldItems union select PeriodEnd from SoldItems union select PeriodStart from Quantity union select PeriodEnd from Quantity
), Periods as (
    select d1.DateVal as StartDate, d2.DateVal as EndDate
    from Dates d1 inner join Dates d2 on d1.DateVal < d2.DateVal left join Dates d3 on d1.DateVal < d3.DateVal and d3.DateVal < d2.DateVal where d3.DateVal is null
), QuantitiesSold as (
    select StartDate,EndDate,COALESCE(SUM(si.SoldItems),0) as Quantity
    from Periods p left join SoldItems si on p.StartDate < si.PeriodEnd and si.PeriodStart < p.EndDate
    group by StartDate,EndDate
)
select StartDate,EndDate,q.Quantity - qs.Quantity
from QuantitiesSold qs inner join Quantity q on qs.StartDate < q.PeriodEnd and q.PeriodStart < qs.EndDate

И результат:

StartDate   EndDate (No column name)
2010-01-01  2010-01-05  5
2010-01-05  2010-01-06  3
2010-01-06  2010-01-07  2
2010-01-07  2010-01-08  4
2010-01-08  2010-01-15  5

Объяснение: Я использую три общих табличных выражения. Первая (Даты) собирает все даты, о которых мы говорим, из двух задействованных таблиц. Второй (Периоды) выбирает последовательные значения из Даты CTE. А третий (QuantitiesSold) затем находит элементы в таблице SoldItems, которые перекрывают эти периоды, и суммирует их итоги. Все, что остается во внешнем выборе, - это вычесть эти количества из общего количества, хранящегося в Таблице количеств

0 голосов
/ 28 октября 2010

Дэмиен

Исходя из вашего решения, я также хотел получить аккуратный дисплей для StockItems без перекрытия дат. Как насчет этого решения?

CREATE TABLE [dbo].[SoldItems](
    [PeriodStart] [datetime] NOT NULL,
    [PeriodEnd] [datetime] NOT NULL,
    [SoldItems] [int] NOT NULL
) ON [PRIMARY]


INSERT INTO SoldItems (PeriodStart,PeriodEnd,SoldItems)
SELECT '20100105','20100106',2 union all
SELECT '20100105','20100108',3 union all
SELECT '20100115','20100116',1 union all
SELECT '20100101','20100120',10


;WITH Dates as (
    select PeriodStart as DateVal from SoldItems
    union 
    select PeriodEnd from SoldItems 
    union
    select PeriodStart from Quantity
    union
    select PeriodEnd from Quantity

), Periods as (
    select d1.DateVal as StartDate, d2.DateVal  as EndDate
    from Dates d1 
    inner join Dates d2 on d1.DateVal < d2.DateVal 
    left join Dates d3 on d1.DateVal < d3.DateVal and 
    d3.DateVal < d2.DateVal where d3.DateVal is null

), QuantitiesSold as (
    select StartDate,EndDate,SUM(si.SoldItems) as Quantity
    from Periods p left join SoldItems si on p.StartDate < si.PeriodEnd and si.PeriodStart < p.EndDate
    group by StartDate,EndDate
)

select StartDate,EndDate, qs.Quantity
from QuantitiesSold qs
where qs.quantity is not null
0 голосов
/ 26 октября 2010

Дэмиен

Я пытаюсь полностью понять ваше решение и проверить его на большом количестве данных, но я получаю следующие ошибки для вашего кода.

Сообщение 102, Уровень 15, Состояние 1, Строка 20

Неверный синтаксис рядом с датами.

Сообщение 102, Уровень 15, Состояние 1, Строка 22 * ​​1009 *

Неверный синтаксис рядом с ','.

Сообщение 102, Уровень 15, Состояние 1, Строка 25

Неверный синтаксис рядом с ','.

0 голосов
/ 22 октября 2010

Джон, ты мог бы сделать цикл WHILE.Объявите и инициализируйте 2 переменные перед циклом, одна из которых является датой начала, а другая - датой окончания.Ваш цикл будет выглядеть следующим образом:

WHILE(@StartEnd <= @EndDate)
BEGIN
  --processing goes here
  SET @StartEnd = @StartEnd + 1
END

Вам нужно будет сохранить определения периодов в другой таблице, чтобы вы могли извлекать их и выводить строки, когда это необходимо, во временную таблицу.

Дайте мне знать, если вам нужны более подробные примеры или у меня неправильный конец флешки!

...