powered by simpleCommunicator - 2.0.53     © 2025 Programmizd 02
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Оптимизация запроса
17 сообщений из 17, страница 1 из 1
Оптимизация запроса
    #39919671
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Здравствуйте!

Помогите, пожалуйста, с оптимизацией запроса.
Есть 2 таблицы.

1) Таблица типов товаров (идентификатор типа, наименование типа).
Код: plaintext
1.
2.
3.
A_ID	A_STR
1	Тип 1
2	Тип 2

2) Таблица товаров (ID - идентификатор товара, BOX - номер партии, A_ID - тип товара, OTHER - тип товара, при его отсутствии в Справочнике №1).
Код: plaintext
1.
2.
3.
4.
5.
6.
ID	BOX	A_ID	OTHER
1	1	1	null
2	1	1	null
3	2	null	иной тип
4	3	2	null
5	3	null	null
Не допускается одновременное указание типов A_ID и OTHER.

Задача. Для каждой партии товаров вывести число товаров в ней и тип товара, если он совпадает для всех товаров в этой партии.
Получается:
Код: plaintext
1.
2.
3.
4.
BOX	A_STR		count
1	тип 1		2
2	иной тип	1
3	null		2

Решил следующим образом, но дико не нравится 2 подзапроса для определения второго столбца A_STR (долго работает на больших объемах данных).
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
declare @A TABLE (A_ID int, A_STR varchar(100))
insert @A values (1, 'тип 1')
insert @A values (2, 'тип 2')

declare @T TABLE (ID int, BOX int, A_ID int, OTHER varchar(100))
insert @T values (1, 1, 1, NULL)
insert @T values (2, 1, 1, NULL)
insert @T values (3, 2, NULL, 'иной тип')
insert @T values (4, 3, 2, NULL)
insert @T values (5, 3, NULL, NULL)

select	BOX,
	case
	  when ( select count(*) from ( select distinct A_ID, OTHER from @T where BOX = T.BOX ) as d) <= 1
	    then (	select top 1 isnull(A_STR, OTHER)
			from @T as T2
			left join @A as A on A.A_ID = T2.A_ID
			where T2.BOX = T.BOX
		   )
	  else null
	end as [A_STR],
	( select count(*) from @T where BOX = T.BOX ) as [count]
from	@T AS T
group by BOX



Запрос явно не оптимален, дико туплю)) Комрады, просьба подсказать)
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919685
invm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Tketano
A_ID - тип товара, OTHER - тип товара, при его отсутствии в Справочнике №1).
Tketano
Задача. Для каждой партии товаров вывести число товаров в ней и тип товара, если он совпадает для всех товаров в этой партии.
Тогда каким образом в результат попал BOX 3?

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
select
 t.BOX, min(b.A_STR), count(*)
from
 @T t left join
 @A a on a.A_ID = t.A_ID cross apply
 (select isnull(a.A_STR, t.OTHER)) b(A_STR)
group by
 t.BOX
having
 count(distinct isnull(b.A_STR, '')) = 1;
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919691
felix_ff
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Tketano,

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
declare @A TABLE (A_ID int, A_STR varchar(100))
insert @A values (1, 'тип 1')
insert @A values (2, 'тип 2')

declare @T TABLE (ID int, BOX int, A_ID int, OTHER varchar(100), index ix clustered (BOX))
insert @T values (1, 1, 1, NULL)
insert @T values (2, 1, 1, NULL)
insert @T values (3, 2, NULL, 'иной тип')
insert @T values (4, 3, 2, NULL)
insert @T values (5, 3, NULL, NULL)

select	BOX,
	case
	  when ( select count(*) from ( select distinct A_ID, OTHER from @T where BOX = T.BOX ) as d) <= 1
	    then (	select top 1 isnull(A_STR, OTHER)
			from @T as T2
			left join @A as A on A.A_ID = T2.A_ID
			where T2.BOX = T.BOX
		   )
	  else null
	end as [A_STR],
	( select count(*) from @T where BOX = T.BOX ) as [count]
from	@T AS T
group by BOX



;with x as (
    select
          t.box, 
          count(1) as cnt,
          avg(cast(isnull(t.a_id, 1.) as decimal)) as [avg],
          max(t.a_id) as [type_id],
          max(t.OTHER) as [type]
    from @T t
    group by t.box
)
select
      x.box,
      iif(x.[avg] = isnull(x.[type_id],1.), isnull(a.[A_STR], x.[type]), NULL) as [A_STR],
      x.cnt
from x
    left join @A a on a.[A_ID] = x.[type_id];
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919692
Фотография court
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
;with cte as (
	select
		t.ID
		,t.BOX
		,isnull(a.A_STR,t.OTHER) as A_STR
		,first_value(isnull(a.A_STR,t.OTHER))over(partition by t.BOX order by isnull(a.A_STR,t.OTHER)) as flag1
		,first_value(isnull(a.A_STR,t.OTHER))over(partition by t.BOX order by isnull(a.A_STR,t.OTHER) desc) as flag2
	from @T t left join @A a on t.A_ID = a.A_ID
)
select
	BOX
	,case when isnull(flag1,'null') <> isnull(flag2,'null') then null else flag1 end as A_STR
	,count(ID) as [count]
from cte
group by
	BOX
	,case when isnull(flag1,'null') <> isnull(flag2,'null') then null else flag1 end
order by 1, 2
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919739
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
with x as ( select BOX
                 , min(A_ID) as A_ID
                 , min(iif(A_ID is null, OTHER, null)) as OTHER
                 , count(A_ID) as cntA
                 , count(OTHER) as cntO
                 , count(distinct A_ID) as dcntA
                 , count(distinct OTHER) as dcntO
              from @T
              group by BOX
          )
select BOX
     , iif(dcntA + dcntO = 1, A_ID, null) as A_ID
     , iif(dcntA + dcntO = 1, OTHER, null) as OTHER
     , cntA+cntO as cnt
  from x
;
-- надеюсть присоединить @A не проблема
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919740
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Впрочем, так будет симпотичнее

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
with t as ( select BOX, A_ID, iif(A_ID is null, isnull(OTHER, 'null'), null) as OTHER from @T )
   , x as ( select BOX
                 , min(A_ID) as A_ID
                 , min(OTHER) as OTHER
                 , count(A_ID) as cntA
                 , count(OTHER) as cntO
                 , count(distinct A_ID) as dcntA
                 , count(distinct OTHER) as dcntO
              from t
              group by BOX
          )
select BOX
     , iif(dcntA + dcntO = 1, A_ID, null) as A_ID
     , iif(dcntA + dcntO = 1, OTHER, null) as OTHER
     , cntA+cntO as cnt
  from x
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919809
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем спасибо!! Сейчас буду изучать предложенные варианты, сходу не все столбцы в cte пока понятны. Кстати, если необходимо вывести ещё несколько полей типа BOX (сведения для целой партии, которые одинаковы для всех строк в рамках партии), то лучше их добавить сразу в group by после BOX?
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919842
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
invmТогда каким образом в результат попал BOX 3?
Информация о всех партиях (BOX) в любом случае должна попасть в выборку, как и число товаров в ней.

Что-то ни одно решение не работает правильно... Да, еще забыл уточнить, SQL Server 2008. IIF подменить не проблема, но варианты с FIRST_VALUE совсем не подходят =)

Вот пример исходных данных таблицы товаров, где начинается некорректный подсчет:
Код: plaintext
1.
2.
3.
4.
5.
6.
ID	BOX	A_ID	OTHER
1	1	1	null
2	1	1	null
3	2	null	иной тип
4	3	2	null
5	3	null	null

Ожидаемый результат:
Код: plaintext
1.
2.
3.
4.
BOX	A_STR		count
1	тип 1		2
2	иной тип	1
3	null		2

На всякий уточню. Значение null в столбцах A_ID и OTHER интерпретируется как отдельное значение, т.е. если, например, в столбце OTHER всего 2 значения null и '123', то итоговый резальтат будет null.
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919854
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Извиняюсь, ошибся немного в данных.
Код: plaintext
1.
2.
3.
4.
5.
6.
ID	BOX	A_ID	OTHER
1	1	1	null
2	1	1	null
3	2	null	иной тип
4	3	null	null
5	3	null	другой тип

Ожидаемый результат:

Код: plaintext
1.
2.
3.
4.
BOX	A_STR		count
1	тип 1		2
2	иной тип	1
3	null		2

Исходные данные:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
declare @A TABLE (A_ID int, A_STR varchar(100))
insert @A values (1, 'тип 1')
insert @A values (2, 'тип 2')

declare @T TABLE (ID int, BOX int, A_ID int, OTHER varchar(100))
insert @T values (1, 1, 1, NULL)
insert @T values (2, 1, 1, NULL)
insert @T values (3, 2, NULL, 'иной тип')
insert @T values (4, 3, NULL, NULL)
insert @T values (5, 3, NULL, 'другой тип')
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919874
Фотография court
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Tketano
варианты с FIRST_VALUE совсем не подходят =)


Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
;with cte as (
	select
		t.ID
		,t.BOX
		,min(coalesce(a.A_STR,t.OTHER,''))over(partition by t.BOX) as flag1
		,max(coalesce(a.A_STR,t.OTHER,''))over(partition by t.BOX) as flag2
	from @T t left join @A a on t.A_ID = a.A_ID
)
select
	BOX
	,case when flag1 <> flag2 then null else nullif(flag1,'') end as A_STR
	,count(ID) as [count]
from cte
group by
	BOX
	,case when flag1 <> flag2 then null else nullif(flag1,'') end
order by 1, 2
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919992
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aleks222
Впрочем, так будет симпотичнее

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
with t as ( select BOX, A_ID, iif(A_ID is null, isnull(OTHER, 'null'), null) as OTHER from @T )
   , x as ( select BOX
                 , min(A_ID) as A_ID
                 , min(OTHER) as OTHER
                 , count(A_ID) as cntA
                 , count(OTHER) as cntO
                 , count(distinct A_ID) as dcntA
                 , count(distinct OTHER) as dcntO
              from t
              group by BOX
          )
select BOX
     , iif(dcntA + dcntO = 1, A_ID, null) as A_ID
     , iif(dcntA + dcntO = 1, OTHER, null) as OTHER
     , cntA+cntO as cnt
  from x



Если данные двух столбцов одновременно не нужны, то получается такой вариант:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
;with t as ( select BOX, coalesce(a.A_STR, OTHER, '') as A_STR_OTHER from @T as t left join @A as a on a.A_ID = t.A_ID )
    , x as ( select BOX,
                    min(A_STR_OTHER) as A_STR_OTHER,
                    count(A_STR_OTHER) as cntA,
                    count(distinct A_STR_OTHER) as dcntA
             from t
             group by BOX
           )
select BOX,
  case
   when dcntA=1 then nullif(A_STR_OTHER, '')
   else null
  end,
  cntA
from x



Правильно?
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39919995
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
court,

Спасибо. Интересное и рабочее решение на первый взгляд. Причем это самый производительный вариант из всех предложенных на реальных данных...

Большое спасибо! Ушел разбираться в коде запроса 8)
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39920270
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Tketano
aleks222
Впрочем, так будет симпотичнее

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
with t as ( select BOX, A_ID, iif(A_ID is null, isnull(OTHER, 'null'), null) as OTHER from @T )
   , x as ( select BOX
                 , min(A_ID) as A_ID
                 , min(OTHER) as OTHER
                 , count(A_ID) as cntA
                 , count(OTHER) as cntO
                 , count(distinct A_ID) as dcntA
                 , count(distinct OTHER) as dcntO
              from t
              group by BOX
          )
select BOX
     , iif(dcntA + dcntO = 1, A_ID, null) as A_ID
     , iif(dcntA + dcntO = 1, OTHER, null) as OTHER
     , cntA+cntO as cnt
  from x



Если данные двух столбцов одновременно не нужны, то получается такой вариант:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
;with t as ( select BOX, coalesce(a.A_STR, OTHER, '') as A_STR_OTHER from @T as t left join @A as a on a.A_ID = t.A_ID )
    , x as ( select BOX,
                    min(A_STR_OTHER) as A_STR_OTHER,
                    count(A_STR_OTHER) as cntA,
                    count(distinct A_STR_OTHER) as dcntA
             from t
             group by BOX
           )
select BOX,
  case
   when dcntA=1 then nullif(A_STR_OTHER, '')
   else null
  end,
  cntA
from x



Правильно?


Бред.
1. Ты ничего не понял.
2. Если тебе надо быстро - нехрен пихать под группировку соединение таблиц.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
with t as ( select BOX, A_ID, iif(A_ID is null, isnull(OTHER, 'null'), null) as OTHER from @T )
   , x as ( select BOX
                 , min(A_ID) as A_ID
                 , min(OTHER) as OTHER
                 , count(A_ID) as cntA
                 , count(OTHER) as cntO
                 , count(distinct A_ID) as dcntA
                 , count(distinct OTHER) as dcntO
              from t
              group by BOX
          )
    , y as (select BOX
                 , iif(dcntA + dcntO = 1, A_ID, null) as A_ID
                 , iif(dcntA + dcntO = 1, OTHER, null) as OTHER
                 , cntA + cntO as cnt
              from x
           )
    select y.BOX, isnull(a.A_STR, OTHER) as A_STR, y.cnt
      from y left outer join @a as a on a.A_ID = y.A_ID 
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39920271
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Эквивалентное преобразование к 2008

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
with t as ( select BOX, A_ID, case when A_ID is null then isnull(OTHER, 'null')  end as OTHER from @T )
   , x as ( select BOX
                 , min(A_ID) as A_ID
                 , min(OTHER) as OTHER
                 , count(A_ID) as cntA
                 , count(OTHER) as cntO
                 , count(distinct A_ID) as dcntA
                 , count(distinct OTHER) as dcntO
              from t
              group by BOX
          )
    , y as (select BOX
                 , case when dcntA + dcntO = 1 then A_ID end as A_ID
                 , case when dcntA + dcntO = 1 then OTHER end as OTHER
                 , cntA + cntO as cnt
              from x
           )
    select y.BOX, isnull(a.A_STR, OTHER) as A_STR, y.cnt
      from y left outer join @a as a on a.A_ID = y.A_ID
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39920607
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
court
Tketano
варианты с FIRST_VALUE совсем не подходят =)


Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
;with cte as (
	select
		t.ID
		,t.BOX
		,min(coalesce(a.A_STR,t.OTHER,''))over(partition by t.BOX) as flag1
		,max(coalesce(a.A_STR,t.OTHER,''))over(partition by t.BOX) as flag2
	from @T t left join @A a on t.A_ID = a.A_ID
)
select
	BOX
	,case when flag1 <> flag2 then null else nullif(flag1,'') end as A_STR
	,count(ID) as [count]
from cte
group by
	BOX
	,case when flag1 <> flag2 then null else nullif(flag1,'') end
order by 1, 2



А какой смысл flag1 и flag2 втягивать в cte? Почему не вытащить isnull(a.A_STR,t.OTHER), а затем применить к этому значению агрегатные функции? Типа так:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
;with cte as (
	select t.ID
	      ,t.BOX
	      ,isnull(a.A_STR,t.OTHER) as A_STR
	from @T t left join @A a on t.A_ID = a.A_ID
)
select
	BOX
	,case when min(isnull(A_STR, '')) <> max(isnull(A_STR, '')) then null else min(A_STR) end as A_STR
	,count(ID) as [count]
from cte
group by
	BOX
order by 1, 2


Ну и по сути cte становится уже не нужна, можно в один select все вывести. Или я не понял идею.
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39920648
Tketano
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
aleks222
Эквивалентное преобразование к 2008

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
with t as ( select BOX, A_ID, case when A_ID is null then isnull(OTHER, 'null')  end as OTHER from @T )
   , x as ( select BOX
                 , min(A_ID) as A_ID
                 , min(OTHER) as OTHER
                 , count(A_ID) as cntA
                 , count(OTHER) as cntO
                 , count(distinct A_ID) as dcntA
                 , count(distinct OTHER) as dcntO
              from t
              group by BOX
          )
    , y as (select BOX
                 , case when dcntA + dcntO = 1 then A_ID end as A_ID
                 , case when dcntA + dcntO = 1 then OTHER end as OTHER
                 , cntA + cntO as cnt
              from x
           )
    select y.BOX, isnull(a.A_STR, OTHER) as A_STR, y.cnt
      from y left outer join @a as a on a.A_ID = y.A_ID



Хм, видимо действительно не понял. Т.е. идея вначале проверить уникальность A_ID (как числа) и столбца OTHER без лишних связок с другими таблицами и только потом подтянуть другие данные?

Дополнительно 2 вопроса:
1) Если в таблице Товаров кроме BOX есть еще BOX_NUMBER (отображаемый номер партии) и он идентичен для всех строк с одним и тем же BOX, то на каком этапе его лучше вытягивать? В group by таблицы x (group by BOX, BOX_NUMBER)?
2) Если столбец OTHER на самом деле делится на 2 столбца: строка и дата, которые соединяются для отображения в специальном формате, то лучше эти данные сразу соединить в таблице t (OTHER_STR+' '+CONVERT(char(10), OTHER_DATE, 104)) или адаптировать запрос с учетом нового типизированного столбца даты и соединить данные в итоговом селекте?
...
Рейтинг: 0 / 0
Оптимизация запроса
    #39920869
aleks222
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Tketano
1) Если в таблице Товаров кроме BOX есть еще BOX_NUMBER (отображаемый номер партии)
Код: sql
1.
 и он идентичен для всех строк с одним и тем же 

BOX, то на каком этапе его лучше вытягивать? В group by таблицы x (group by BOX, BOX_NUMBER)?
2) Если столбец OTHER на самом деле делится на 2 столбца: строка и дата, которые соединяются для отображения в специальном формате, то лучше эти данные сразу соединить в таблице t (OTHER_STR+' '+CONVERT(char(10), OTHER_DATE, 104)) или адаптировать запрос с учетом нового типизированного столбца даты и соединить данные в итоговом селекте?


1. min/max(BOX_NUMBER) group by BOX
2. пофиг.
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / Microsoft SQL Server [игнор отключен] [закрыт для гостей] / Оптимизация запроса
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]