Помогите сформулировать запрос!

uaggster
Дата: 07.02.2013 20:06:46
Имеется некая таблица create table t (a int identity(1,1) primary key, b int, c int check (c between 0 and N)), где N - некое произвольное (не большое) число (ну, 5, например).
Столбец b заполняется произвольными повторяющимися числами (на самом деле, это значения некоего внешнего ключа, но это - не важно).

"Ключом N-ки чисел" будем называть такое число из столбца b, для которого в таблице t имеется в точности одна запись со значением в столбце с = 0, + в точности одна запись со значением = 1 + ... + в точности одна запись со значением = N, т.е. если для числа из столбца b представлен весь набор значений из столбца с, без изъянов и дублей.

Вывести все записи из таблицы t, если b НЕ является "ключом N-ки".

... В принципе, если не удается решить всё это одним запросом, то согласен на конструкцию с временными таблицами.
+ хотелось бы иметь некое общее решение, для произвольного N, а не наворачивать N подзапросов с exists.
qwerty112
Дата: 07.02.2013 21:15:19
uaggster
Вывести все записи из таблицы t, если b НЕ является "ключом N-ки".

имхо, обратная этой задаче - это то, что называется реляц.делением,
поищите по форуму - бывало ...

select b
from t
group by b
having count (*) = count(distinct a)
   and count (*) = N+1


и "прямая" задача, соотв. :
select * from t
where b not in 
(select b
from t
group by b
having count (*) = count(distinct a)
   and count (*) = N+1)
invm
Дата: 07.02.2013 23:38:30
declare @n int = 4;

declare @t table (a int identity(1,1) primary key, b int, c int check (c between 0 and 4));

insert into @t
 (b, c)
values
 (4, 0), (4, 1), (4, 2), (4, 3), (4, 4),
 (1, 0), (1, 1), (1, 2), (1, 3),
 (2, 0), (2, 1), (2, 2),
 (3, 0), (3, 1), (3, 1), (3, 3), (3, 3);

with x as
( 
 select
  *,
  @n - (dense_rank() over (partition by b order by c) - 1) as r1,
  dense_rank() over (partition by b order by c desc) - 1 as r2
 from
  @t
)
select
 a, b, c
from
 x
where
 r1 <> r2;
qwerty112
Дата: 08.02.2013 01:26:27
fix
qwerty112
и "прямая" задача, соотв. :
select * from t
where b not in 
(select b
from t
group by b
having count (*) = count(distinct c)
   and count (*) = N+1)
Cygapb-007
Дата: 08.02.2013 01:40:42
invm, при всем уважении, но RANK для повторов одинаков - после добавления, например, (4,1), по-прежнему 4 будет считаться ключем.
aleks2
Дата: 08.02.2013 09:57:29
declare @n int = 4;

declare @t table (a int identity(1,1) primary key, b int, c int check (c between 0 and 4));

insert into @t
 (b, c)
values
 (4, 0), (4, 1), (4, 2), (4, 3), (4, 4),
 (1, 0), (1, 1), (1, 2), (1, 3),
 (2, 0), (2, 1), (2, 2),
 (3, 0), (3, 1), (3, 1), (3, 3), (3, 3);

-- ну эта просто стационарной таблицей можно сделать
declare @c table(c int primary key clustered);
while @n>=0 begin
  insert @c values(@n);
  set @n = @n-1;
end;

-- и фсего то два exists
select * from @t t
where exists( select * from @t tt where tt.b=t.b and tt.c=t.c and tt.a<>t.a )
      or
      exists( select * 
                from @c c 
                     left outer join 
                     (select * from @t tt where tt.b=t.b) ttt 
                     on ttt.c=c.c 
                where ttt.c is null
            )
            
select * from @t t
where exists( select * from @t tt where tt.b=t.b and tt.c=t.c and tt.a<>t.a )
      or
      exists( select c from @c
              except
              (select c from @t tt where tt.b=t.b)
             )            
Cygapb-007
Дата: 08.02.2013 10:11:37
1) то же возражение
(4, 0), (4, 1), (4, 2), (4, 3), (4, 4), (4, 4),

2) самый первый вариант куда как лаконичнее
invm
Дата: 08.02.2013 10:18:30
Поправки, с учетом замечания Cygapb-007:
declare @n int = 4;

declare @t table (a int identity(1,1) primary key, b int, c int check (c between 0 and 4));

insert into @t
 (b, c)
values
 (4, 0), (4, 1), (4, 2), (4, 3), (4, 4),
 (1, 0), (1, 1), (1, 2), (1, 3),
 (2, 0), (2, 1), (2, 2),
 (3, 0), (3, 1), (3, 1), (3, 3), (3, 3);

with x as
( 
 select
  *,
  @n - (dense_rank() over (partition by b order by c) - 1) as r1,
  dense_rank() over (partition by b order by c desc) - 1 as r2,
  row_number() over (partition by b order by a, c) +
  row_number() over (partition by b order by a desc, c desc) as n
 from
  @t
)
select
 a, b, c
from
 x
where
 r1 <> r2 or n > @n + 2;
Добрый Э - Эх
Дата: 08.02.2013 10:53:19
uaggster,

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

select top 1 with ties *
  from t
 order by case 
            when count(1) over(partition by b) != N + 1
              or sum(c) over(partition by b) != ((N + 1)*(N))/2 
            then 1 
          end desc

Нигде не проверял, за работоспособность не ручаюсь :)
Добрый Э - Эх
Дата: 08.02.2013 10:54:49
Добрый Э - Эх,

Хотя, к чему эти отрицания и обратная сортировка?

Можно и так:
select top 1 with ties *
  from t
 order by case 
            when count(1) over(partition by b) = 4 + 1
              and sum(c) over(partition by b) != ((4 + 1)*(4))/2 
            then 1 
          end