Может ли это while быть преобразовано в запрос на основе набора?

#sql #sql-server #tsql #sql-server-2012

#sql #sql-сервер #tsql #sql-server-2012

Вопрос:

У меня есть этот запрос:

 if OBJECT_ID('tempdb..#tempA') is not null drop table #tempA

create table #tempA
(
    tempid varchar(5),
    tempdate smalldatetime
)

declare @loopdate smalldatetime
set @loopdate = '4/2/2013'

while (@loopdate <= '4/28/2013')
begin
    --Purpose is to get IDs not in TableB for each date period
    insert into #tempA (tempid, tempdate)
    select storeid, @loopdate
    from
    (
        select tableAid
        from tableA
        except
        select tableBid
        from tableB
        where tableBdate = @loopdate
    ) as idget

    set @loopdate = DATEADD(day, 1, @loopdate)
end
  

Есть ли способ создать цикл while на основе набора или это лучшее, что можно сделать?

РЕДАКТИРОВАТЬ: внесены изменения для корректности

РЕДАКТИРОВАТЬ: конечный результат

 ID1 4/2/2014
ID2 4/2/2014
ID4 4/2/2014
ID2 4/3/2014
ID1 4/4/2014
ID5 4/4/2014
ID3 4/5/2014
  

Комментарии:

1. Я почти уверен, что это возможно, но не могли бы вы опубликовать пример ваших исходных данных и желаемого конечного результата?

2. Вы не вставляете значение для tempdate . Это не работает.

3. То, как вы это делаете, может быть лучшим способом. Решение set должно было бы сгенерировать этот набор дат.

Ответ №1:

Все еще цикл, но, возможно, немного более эффективный

 while (@loopdate <= '4/28/2013')
begin
    --Purpose is to get IDs not in TableB for each date period
    insert into #tempA (tempid, tempdate)
    select storeid, @loopdate
    from
    (
        select tableAid
          from tableA
          left join tableB 
            on tableB.tableBid = tableA.tableAid
           and tableB.tableBdate = @loopdate 
         where tableB.tableBid is null
    ) as idget

    set @loopdate = DATEADD(day, 1, @loopdate)
end
  

Это требует некоторой работы, но может полностью помочь вам с набором

 ;WITH Days
as
(
    SELECT cast('4/2/2013' AS datetime ) as 'Day'
    UNION ALL
    SELECT DATEADD(DAY,  1, Day) as 'Day'
      FROM Days
     where [DAY] <= '4/28/2013'
)
SELECT tableA.tableAid, Days.[Day] 
  from Days 
  left join tableB 
    on tableB.tableBdate = Days.[Day]
  full join tableA 
    on tableB.tableBid = tableA.tableAid 
 where tableB.tableBid is null
  

Комментарии:

1. Действительно ли левое соединение более эффективно, чем исключение? Просто интересно.

2. @dotnetN00b может ли Except использовать индекс?

Ответ №2:

это зависит от того, есть ли в таблице not дата, если нет, то:

 WITH DateList(DateDay) AS 
(  
     SELECT CAST('2013-04-28' AS DATETIME)
        UNION ALL
    SELECT DATEADD(DAY, DATEDIFF(DAY,0,DATEADD(DAY, -1, DateDay)),0)  
    FROM DateList  
    WHERE DateDay between '2013-04-03' and '2013-04-28'
)  
SELECT DISTINCT
    tableAid
    , DateDay 
FROM DateList
  cross join #tableA a
  left join #tableB b
    on tableAid = b.tableBid
    and b.tableBdate = DateDay
where
    b.tableBid is null
ORDER BY
    DateDay ASC
  

Ответ №3:

 insert into #tempA (tempid, tempdate)
select tableAid, tableAdate
from tableA
except
select tableBid,tableBdate
from tableB
where tableBdate >= '4/2/2013' and tableBdate <= '4/28/2013';
  

Ответ №4:

Вы можете включить диапазон дат в условное предложение, как показано ниже:

 insert into #tempA (tempid, tempdate)
select tableAid
from tableA
except
select tableBid
from tableB
where tableBdate >= '4/2/2013' and tableBdate <= '4/28/2013';