Форум программистов, компьютерный форум, киберфорум
Microsoft SQL Server
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.63/8: Рейтинг темы: голосов - 8, средняя оценка - 4.63
0 / 0 / 0
Регистрация: 08.08.2019
Сообщений: 25

Отбор макс. значений из таблицы

26.12.2019, 11:03. Показов 1696. Ответов 13

Студворк — интернет-сервис помощи студентам
Здравствуйте, форумчане!
Есть таблица с полями Номенклатура, ДатаПокупки, Цена.
Первичного ключа нет, значения в полях Номенклатура и ДатаПокупки могут повторяться.
Надо, ориентируясь на дату покупки, отобрать самые последние цены.
Вот как я это сделал:
T-SQL
1
2
3
4
select distinct Номенклатура, МаксДата, Цена from
 ( select Номенклатура, ДатаПокупки, max(ДатаПокупки) over (partition by Номенклатура) as МаксДата, Цена
   from dbo.Поставки
 ) as d where ДатаПокупки = МаксДата
Можно сделать это более коротким кодом, без вложения selectОВ?
Подскажите, пожалуйста.
Заранее благодарен.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
26.12.2019, 11:03
Ответы с готовыми решениями:

Отбор в таблице значений по условию четных и нечетных значений
Здравствуйте, учусь работать с таблицами значений, дали задачу, написать форму и что бы в одной тч выводились номера домов и сумма...

Как установить отбор для поля таблицы по другому полю той же таблицы реквизитов связанных через регистр сведений
Здравствуйте, уважаемые специалисты. Подскажите как установить отбор (скрин формы прилагается) при выборе в поле...

отбор значений
Помогите пожалуйста организовать отбор в форме касса. необходимо, чтобы после выбора периода в форме отображались данные соответствующие...

13
5962 / 4538 / 1094
Регистрация: 29.08.2013
Сообщений: 28,150
Записей в блоге: 3
26.12.2019, 11:08
можно так

T-SQL
1
2
3
select t.*, a.*
from (select Номенклатура from dbo.Поставки group by Номенклатура ) t
outer apply (select top 1 * from dbo.Поставки where t.Номенклатура = Номенклатура order by ДатаПокупки desc) a
но если бы номенклатура была справочником с индексом было бы лучше
1
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
26.12.2019, 11:51
Цитата Сообщение от AleksFromBK Посмотреть сообщение
значения в полях Номенклатура и ДатаПокупки могут повторяться.
И какая тогда последняя цена?
0
0 / 0 / 0
Регистрация: 08.08.2019
Сообщений: 25
26.12.2019, 13:29  [ТС]
qwertehok, спасибо за оперативный ответ.
Хочу поделиться с вами результатами.
Может быть это будет для вас интересно.

Сравнил оба варианта - мой приблизительно на треть быстрее.
Правда результирующий набор всего 150000 записей,
а исходный около 600 тысяч, так что о быстродействии судить можно весьма условно.

В вашем варианте заменил group by на distinct -
ничего по времени не дало. А вот замена outer на cross
сравняла наши варианты по быстродействию.
Cross работает быстрее и в данном случае как раз годится,
поскольку "табличная функция" (второй селект) всегда
гарантированно возвращает одну запись.

Я ничего не сообщил об условиях отбора данных.
На самом деле они есть:
T-SQL
1
where КодФирмы = 2 and КодСклада = 3
После их внедрения результирующий набор сокращается до 50 тысяч.
При этом ваш вариант начинает проигрывать в быстродействии,
потому что у вас получаются два селекта с условием, а у меня - один.

Но эстетически ваш вариант мне нравится больше.
Ещё раз спасибо.

Добавлено через 24 минуты
invm, совершенно верно подметили.
Крайне редко (на моих данных), но может случиться вот
такая вот картина:
Ном1 Дата1 Цена1
Ном1 Дата1 Цена2
Ном1 Дата1 Цена3
Ном2 Дата5 Цена5 ...
И distinct в данном случае не спасает. Я закрываю на это глаза.
Можно было бы добавить ещё один внешний селект:
T-SQL
1
select Номенклатура, МаксДата, max(Цена) Цена from ...
но не хочется утяжелять конструкцию, тем более что:
- заказчик сам не знает, какую цену в данном случае брать, - меньшую, большую, среднюю;
- результат отбора используется в последующих сложных расчётах, где и можно
принять решение как поступить.

P.S. Кстати, в варианте qwertehok дублирования, подобного
указанному выше, нет, что хорошо, но цена всё равно возвращается как бог на душу положит.
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
26.12.2019, 13:42
Цитата Сообщение от AleksFromBK Посмотреть сообщение
В вашем варианте заменил group by на distinct -
ничего по времени не дало.
Distinct и есть group by в большинстве случаев.
Цитата Сообщение от AleksFromBK Посмотреть сообщение
Сравнил оба варианта - мой приблизительно на треть быстрее.
Какое быстродействие? В одном distinct, в другом select top (1) на каждую строку внешнего источника...
Пробуйте так
T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
with t as
(
 select
  Номенклатура, Дата, Цена,
  row_number() over (partition by Номенклатура order by Дата desc) as rn
 from
  dbo.Поставки
 where
  КодФирмы = 2 and КодСклада = 3
)
select
 Номенклатура, Дата, Цена
from
 t
where
 rn = 1;
Если хотите еще ускорить, то нужен индекс (Номенклатура, Дата desc) include (Цена, КодФирмы, КодСклада). Тогда в запросе будет только сканирование этого индекса, без сортировок.
1
0 / 0 / 0
Регистрация: 08.08.2019
Сообщений: 25
26.12.2019, 14:51  [ТС]
invm, спасибо за ваше участие, вы всегда помогаете.
Вы удивляетесь моему рассказу о быстродействии.
Да, объёмы не те, на которых можно что-то сравнить, всё очень приблизительно.
Тем не менее я говорю о том, что вижу своими глазами...

Вы пишите "Distinct и есть group by в большинстве случаев."
Я же всегда придерживаюсь правила не применять, если не надо,
избыточного по мощности инструментария.

Что касается индексов, то на номенклатуру и дату они разумеется есть.
А вот поля КодФирмы и КодСклада не проиндексированы, потому что
значения этих полей имеют очень низкую селективность и их
индексирование практически ничего не даёт, кроме увеличения объема.

Ваш вариант с row_number() прекрасен, я его тоже имел в виду,
но всё значительно сложнее того, о чём я написал.
Я просто упростил задачу до максимума, чтобы сильно не
нагружать коллег по профессии своими проблемами.

Дело в том, что последнюю цену надо найти не в одной таблице, а в трёх.
Вот реальный фрагмент процедуры, готовящей таблицу заказов:
T-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
update dbo.Р_ЗаказатьНаЕП set Цена = d.Цена
from dbo.Р_ЗаказатьНаЕП z
join
(select distinct КодНоменклатуры, Цена from
    (select КодНоменклатуры, Дата, max(Дата) over (partition by КодНоменклатуры) as МаксДата, Цена from
        (select distinct КодНоменклатуры, МДО as Дата, ЦенаБезНДСРубли as Цена from
            ( select КодНоменклатуры, ДатаПокупки,
                           max(ДатаПокупки) over (partition by КодНоменклатуры) as МДО, ЦенаБезНДСРубли
               from dbo.Остатки where КодСклада = 3 and Фирма = 2
             ) as d where ДатаПокупки = МДО
             union all
             select distinct КодНоменклатуры, МДЗ, ЦенаПокупки from
             ( select КодНоменклатуры, ДатаЗаказа, 
                            max(ДатаЗаказа) over (partition by КодНоменклатуры) as МДЗ, ЦенаПокупки
                from dbo.Заказы where ТипЗаказа = 'Склад' and КодФирмы = 2
             ) as d where ДатаЗаказа = МДЗ
             union all
             select distinct КодНоменклатуры, МДП, ЦенаБезНДСРубли from
             ( select КодНоменклатуры, ДатаПоставки, 
                            max(ДатаПоставки) over (partition by КодНоменклатуры) as МДП, ЦенаБезНДСРубли
                from dbo.Поставки where КодФирмы = 2 and КодСклада = 3
             ) as d where ДатаПоставки = МДП
        ) r
    ) i where Дата = МаксДата
) d on z.Номенклатура = d.КодНоменклатуры
Если интересно - вот объёмы:
Р_ЗаказатьНаЕП - 5503, Остатки - 136471, Заказы - 530096, Поставки - 587433
Время выполнения фрагмента - 2 сек. (то, что рисует SQL).
Работаю удалённо, на каких мощностях - представления не имею.
Время меня устраивает. Не устраивает громоздкость этой конструкции.

Спасибо вам, invm, за "разбор полётов".
0
5962 / 4538 / 1094
Регистрация: 29.08.2013
Сообщений: 28,150
Записей в блоге: 3
26.12.2019, 15:01
Цитата Сообщение от AleksFromBK Посмотреть сообщение
Время выполнения фрагмента - 2 сек
а данных сколько?

Цитата Сообщение от AleksFromBK Посмотреть сообщение
Не устраивает громоздкость этой конструкции.
перепиши на временных таблицах - будет короче
1
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
26.12.2019, 15:05
Цитата Сообщение от AleksFromBK Посмотреть сообщение
Вы пишите "Distinct и есть group by в большинстве случаев."
Я же всегда придерживаюсь правила не применять, если не надо,
избыточного по мощности инструментария.
Distinct - это group by по всем столбцам.
Цитата Сообщение от AleksFromBK Посмотреть сообщение
Что касается индексов, то на номенклатуру и дату они разумеется есть.
А вот поля КодФирмы и КодСклада не проиндексированы, потому что
значения этих полей имеют очень низкую селективность и их
индексирование практически ничего не даёт, кроме увеличения объема.
Отдельные индексы по дате и номенклатуре для данного запроса бесполезны во всех его вариантах. Про индексы по КодФирмы и КодСклада вообще ничего не писалось.
Какой именно нужен индекс для данного запроса я уже писал.

Как сделать быстро я тоже уже писал. Ничего не мешает оформить это как представления для трех таблиц.
И в итоговом запросе работать с этими представлениями.
1
0 / 0 / 0
Регистрация: 08.08.2019
Сообщений: 25
26.12.2019, 16:05  [ТС]
Цитата Сообщение от invm Посмотреть сообщение
Distinct - это group by по всем столбцам.
Даже не понимаю, о чём мы спорим.
Что distinct по всем столбцам, что group by по тем же столбцам, если надо
получить уникальные записи. Только group by позволяет
ещё что-то сделать с данными, попавшими в группу. Но если не нужно
ничего делать с данными группы, а лишь отобрать уникальные значения -
зачем использовать group by?
"Отдельные индексы по дате и номенклатуре для данного запроса бесполезны во всех его вариантах."
Для данного запроса - да.
"Про индексы по КодФирмы и КодСклада вообще ничего не писалось."
Да, я ошибся.
"Какой именно нужен индекс для данного запроса я уже писал."
Тут вот какая проблема. Если бы мои таблицы "жили" долго и по ним ежедневно и помногу
раз выполнялся бы поиск, то конечно надо было создать по максимуму индексы,
ускоряющие отбор данных. Но особенность моей БД в том, что она загружается один
раз ночью. После этого однократно выполняются массовые расчёты (по модели
управления запасами) и на этом всё заканчивается. То есть затраты на индексирование
при загрузке БД могут превосходить выигрыш в быстродействии при одном (двух) считывании
данных из соответствующей таблицы.
"Как сделать быстро я тоже уже писал."
Вы имеете в виду использование CTE-выражения с функцией row_number()?
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
26.12.2019, 16:55
Цитата Сообщение от AleksFromBK Посмотреть сообщение
Даже не понимаю, о чём мы спорим.
Что distinct по всем столбцам, что group by по тем же столбцам, если надо
получить уникальные записи. Только group by позволяет
ещё что-то сделать с данными, попавшими в группу. Но если не нужно
ничего делать с данными группы, а лишь отобрать уникальные значения -
зачем использовать group by?
Я не говорю, что нужно использовать group by вместо distinct
Я говорю, что физически distinct раскрывается либо в group by, либо в distinct sort. Поэтому бессмысленно сравнивать производительность dustinct и group by.
Цитата Сообщение от AleksFromBK Посмотреть сообщение
Но особенность моей БД в том, что она загружается один
раз ночью. После этого однократно выполняются массовые расчёты
Тогда нет смысла заморачиваться производительностью на ваших объемах.
Цитата Сообщение от AleksFromBK Посмотреть сообщение
Вы имеете в виду использование CTE-выражения с функцией row_number()?
Да. Можно и без CTE, через derived table.
1
0 / 0 / 0
Регистрация: 08.08.2019
Сообщений: 25
26.12.2019, 18:32  [ТС]
Цитата Сообщение от invm Посмотреть сообщение
Да. Можно и без CTE, через derived table.
[/QUOTE]

CTE использовать не могу, уже есть в процедуре.
Перешёл на row_number(), работает немного быстрее:

T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    update dbo.Р_ЗаказатьНаЕП set Цена = d.Цена
    from dbo.Р_ЗаказатьНаЕП z
    join --dbo.П_ЦеныЗамен d on z.Номенклатура = d.КодНоменклатуры
    (select distinct КодНоменклатуры, Цена from
    (select КодНоменклатуры, Цена, row_number() over (partition by КодНоменклатуры order by Дата desc) as rn from
    (   select КодНоменклатуры, ДатаПокупки as Дата, ЦенаБезНДСРубли as Цена from
        ( select КодНоменклатуры, ДатаПокупки, ЦенаБезНДСРубли, row_number() over (partition by КодНоменклатуры order by ДатаПокупки desc) as rn
        from dbo.Остатки where КодСклада = 3 and Фирма = 2 ) as d where rn = 1
        union all
        select КодНоменклатуры, ДатаЗаказа, ЦенаПокупки from
        ( select КодНоменклатуры, ДатаЗаказа, ЦенаПокупки, row_number() over (partition by КодНоменклатуры order by ДатаЗаказа desc) as rn
        from dbo.Заказы where ТипЗаказа = 'Склад' and КодФирмы = 2 ) as d where rn = 1
        union all
        select КодНоменклатуры, ДатаПоставки, ЦенаБезНДСРубли from
        ( select КодНоменклатуры, ДатаПоставки, ЦенаБезНДСРубли, row_number() over (partition by КодНоменклатуры order by ДатаПоставки desc) as rn
        from dbo.Поставки where КодФирмы = 2 and КодСклада = 3 ) as d where rn = 1
    )    r
    ) c where rn = 1
    ) d on z.Номенклатура = d.КодНоменклатуры
Создал представление. По времени то же самое, но зато скрыл с глаз месиво кода. (см. коммент в запросе.)
Спасибо.

Добавлено через 10 минут
В предыдущем коде distinct не нужен.
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
26.12.2019, 18:59
Возможно так будет проще и быстрее
T-SQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    update dbo.Р_ЗаказатьНаЕП set Цена = d.Цена
    from dbo.Р_ЗаказатьНаЕП z
    join --dbo.П_ЦеныЗамен d on z.Номенклатура = d.КодНоменклатуры
    (select distinct КодНоменклатуры, Цена from
    (select КодНоменклатуры, Цена, row_number() over (partition by КодНоменклатуры order by Дата desc) as rn from
    (   select КодНоменклатуры, ДатаПокупки as Дата, ЦенаБезНДСРубли as Цена from
        from dbo.Остатки where КодСклада = 3 and Фирма = 2
        union all
        select КодНоменклатуры, ДатаЗаказа, ЦенаПокупки from
        from dbo.Заказы where ТипЗаказа = 'Склад' and КодФирмы = 2
        union all
        select КодНоменклатуры, ДатаПоставки, ЦенаБезНДСРубли from
        from dbo.Поставки where КодФирмы = 2 and КодСклада = 3
    )    r
    ) c where rn = 1
    ) d on z.Номенклатура = d.КодНоменклатуры
1
0 / 0 / 0
Регистрация: 08.08.2019
Сообщений: 25
26.12.2019, 19:46  [ТС]
invm, я проверил ваш последний код.
На вскидку был почти уверен, что он будет работать быстрее.
Казалось бы - зачем нумеровать по трём порциям, когда
можно свалить всё в кучу и одним махом пометить последние даты
и их же потом отобрать. Но!
Вы будете смеяться... Без update, только селект в последнем коде
даёт 2 сек (SQL показывает), предыдущий код - 1 сек. Может быть
и полторы или 1,7 - вы же понимаете, но 2 сек. не показывает.
Я даже кэш чистил - DBCC FREEPROCCACHE для чистоты эксперимента.
В чём причина? В голову приходит только одна мысль: серверу быстрее
записать (куда? - в ОП или во временную БД?) три небольших массива
данных, чем один большой...
0
3614 / 2135 / 756
Регистрация: 02.06.2013
Сообщений: 5,169
26.12.2019, 20:31
AleksFromBK, все что угодно может быть.
Если выложите актуальный план выполнения, можно будет найти причину.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
26.12.2019, 20:31
Помогаю со студенческими работами здесь

Отбор в древе значений
Добрый день. 8.1 учебная Есть вот такой код Запрос = Новый Запрос("ВЫБРАТЬ | ИСТИНА КАК Флажок, ...

Отбор и сортировка значений на листе
На листе Excel в колонках C-D,F-G,I-J содержатся цифровые значения, в колонках E,H - точки для разделения цифр. В колонке K идет текстовое...

Отбор/Фильтр в дереве значений
Всем привет! Итак вопрос: 1. Можно ли (или как) сделать отбор (или установить фильтр) в дереве значений, с сохранением иерархии? ...

ListView. Отбор уникальных значений
Вопрос следующего плана. Есть ListView с пятью колонками. Каким образом можно получить или только уникальные записи или удалить...

Отбор и выделения значений из строк
Доброго времени суток! Прошу помоч с написанием макроса. Таблица в Эксель. В одном из столбцов Range("B5:B5004"), нужно...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
14
Ответ Создать тему
Новые блоги и статьи
Использование SDL3-callbacks вместо функции main() на Android, Desktop и WebAssembly
8Observer8 24.01.2026
Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а привычная функция main(). . .
моя боль
iceja 24.01.2026
Выложила интерполяцию кубическими сплайнами www. iceja. net REST сервисы временно не работают, только через Web. Написала за 56 рабочих часов этот сайт с нуля. При помощи perplexity. ai PRO , при. . .
Модель сукцессии микоризы
anaschu 24.01.2026
Решили писать научную статью с неким РОманом
http://iceja.net/ математические сервисы
iceja 20.01.2026
Обновила свой сайт http:/ / iceja. net/ , приделала Fast Fourier Transform экстраполяцию сигналов. Однако предсказывает далеко не каждый сигнал (см ограничения http:/ / iceja. net/ fourier/ docs ). Также. . .
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь(не выше 3-го порядка) постоянного тока с элементами R, L, C, k(ключ), U, E, J. Программа находит переходные токи и напряжения на элементах схемы классическим методом(1 и 2 з-ны. . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru