Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   украшаем запрос MS SQL (http://forum.oszone.net/showthread.php?t=117274)

pva 15-09-2008 15:18 899840

украшаем запрос MS SQL
 
1. можно ли как-то запустить Query Analyzer (2000) с ярлычка, чтоб он не заново запускался, а создал окошко в уже открытом аналайзере? А то задолбал плодиться, я забываю смотреть, есть он в живых или нет.

2. есть уже заполненная табличка:
Код:

declare @dates1 table (
  recId    integer,  // прочая информация
  groupId  integer, // поле для группировки
  dateIn    datetime,  // начало отрезка времени
  dateOut  datetime); // конец отрезка времени

нужно оставить в ней записи, имеющие хотя бы одно пересечение с другими при одинаковой groupId. Остальные удалить. Я сделал, но мне кажтся чёто как-то некрасиво и медленно, через delete where not exists(...). Сами понимаете, некрасивые самолёты не летают. Есть варианты как причесать запрос?

Busla 15-09-2008 16:31 899925

для MS SQL есть свой раздел форума
2. сразу сделать правильную (необходимую) выборку. Временные таблицы - очень ресурсоёмкое решение, так ли они нужны в данном случае?

pva 16-09-2008 06:46 900421

Цитата:

для MS SQL есть свой раздел форума
Можно тему туда перенести? Я не заметил раздела

Может я неправильно понимаю как оно работает, обрисую задачу:
Есть табличка с оплаченными повременными услугами, примерно миллион-два записей (с 2004 года). Из них нужно выбрать такие записи за последние 4 недели, которые бы оплачивали одну и ту же услугу в одно и то же время, то есть выяснить какие есть перекрытия оплат.
Табличка имеет много всяких полей, я выбрал только нужные 4 поля за последние недели (около 1000 строчек), а потом искал пересечения
Код:

delete from
    @dates
from
    @dates debet1
where
    not exists (
        select * from @dates debet2
        where
            debet1.recId <> debet2.recId
            and debet1.groupId = debet2.groupId
            and debet1.dateIn < debet2.dateOut
            and debet2.dateIn < debet1.dateOut);

А можно ли как-то создать временный view? чтобы съэкономить и упонятнить код

amel27 16-09-2008 13:12 900640

pva
1. Запускать скриптом, который проверяет наличие QA и либо запускает, либо активирует окно и отправляет комбинацию <Ctrl+N>

Busla 16-09-2008 14:58 900755

pva, я бы всё-таки работал с изначальной таблицей
где-то примерно так:
Код:

select groupId, дата, count (*)
  from услуги
  where дата>'2008-08-15'
  group by groupId, дата
  having count (*)>1

на выходе получаем список перекрытий
(в примере не понял соотношение dateIn и dateOut)

pva 16-09-2008 16:55 900877

дело в том, что граница оказания услуг может быть и не кратна дню, пример:
услуга 1: от '2008-01-01 12:00:00' до '2008-01-02 14:00:00'
услуга 2: от '2008-01-01 18:00:00' до '2008-01-02 12:00:00'

Busla 16-09-2008 20:11 901095

т.е. имеем ряд временных диапазонов, нужно выбрать накладывающиеся? - да, группировкой тут не обойтись :-(
хотя всё равно не такой уж и монстрообразный запрос получается:
Код:

select u1.*
  from
    услуги u1, услуги u2
  where
    u1.dateIn>'2008-08-15' and u2.dateIn>'2008-08-15' -- 4 недели назад - лучше вынести в переменную
    and u1.groupId=u2.groupId2 and u1.recId<>u2.recId
    and (u1.dateIn beetwen u2.dateIn and u2.dateOut or u1.dateOut beetwen u2.dateIn and u2.dateOut)


pva 16-09-2008 22:12 901193

Busla, Я приведу пример когда этот вариант не работает:
Код:

and (u1.dateIn beetwen u2.dateIn and u2.dateOut or u1.dateOut beetwen u2.dateIn and u2.dateOut)

    .....время................
  u1    1111111111111111111
  u2        2222222222222
ни начало ни конец u1 не входят в u2.

теперь из каких соображений я делал:
Код:

  u1        11111111111111
  u2          222222222222222

  1. если u2 начнётся после того, как u1 закончится - нет пересечения.
  2. если u1 начнётся после того, как u2 закончится - нет пересечения.
  3. иначе пересекаются.

упрощаем условие:

    not (u2.dateOut <= u1.dateIn or u1.dateOut <= u2.dateIn) -->
--> u2.dateOut > u1.dateIn and u1.dateOut > u2.dateIn -->
--> u1.dateIn < u2.dateOut and u2.dateIn < u1.dateOut

Насчёт выборки действительно ступил - заработался. Всё действиетльно можно одним несложным селектом сделать. На сегодня такой вариант:
Код:

select
    u1.recId,
    u1.groupId,
    u1.dateIn,
    u1.dateOut
from
    услуги u1
    join услуги u2 on (
        getDate() - 4*7 <= u2.dateIn
        and u1.groupId = u2.groupId
        and u1.dateIn < u2.dateOut
        and u2.dateIn < u1.dateOut
        and u1.recId <> u2.recId)
where
    getDate() - 4*7 <= u1.dateIn;
order by
    u1.groupId,
    u1.dateIn


Busla 17-09-2008 14:44 901657

pva, всё правильно. Ты просто забываешь, что помимо
Код:

.....время................
  u1    1111111111111111111
  u2        2222222222222

будет и обратная пара:

  u1        2222222222222
  u2    1111111111111111111


pva 18-09-2008 06:44 902184

не-а, не забываю :-P если список состоит только из этих двух записей, то твой вариант запроса выдаст только одну. А надо обе (это всё необходимо для отчёта, чтобы потом человек посмотел пересечения и принял решение, что из них поправить)

pva 18-09-2008 08:35 902218

остаётся вопрос с временными view. Можно такие сделать? На сколько это критично к ресурсам? у меня услуги - это
Код:

accDebet join accCliPay on (accCliPay.accPayId=accDebet.accPayId and accCliPay.payCod=1)
Временные - это в том смысле, чтобы они уничтожались после выхода из блока, где были созданы, независимо от того успешно прошла операция или нет

Busla 18-09-2008 16:56 902600

Да, запрос у меня получился несколько однобокий.

AFAIK вьюхи интерпритируются примерно как макрос - т.е. хранится только код запроса, и он подставляется в другие запросы - содержимое view никак специально не индексируется, не кешируется и т.п.
Можно использовать не view, а подзапрос:

Код:

select *
  from
    (select * from accDebet join accCliPay on (accCliPay.accPayId=accDebet.accPayId and accCliPay.payCod=1)) as u1,
    (select * from accDebet join accCliPay on (accCliPay.accPayId=accDebet.accPayId and accCliPay.payCod=1)) as u2
  where
    u1.dateIn>'2008-08-15' and u2.dateIn>'2008-08-15' -- 4 недели назад - лучше вынести в переменную
    and u1.groupId=u2.groupId2 and u1.recId<>u2.recId
    and u1.dateIn>=u2.dateIn and u1.dateIn<=u2.dateOut

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

pva 19-09-2008 07:41 903043

когда идентичные - это нормально, нужно обе выдавать. Вот теперь красиво получилось! Пасиба *DRINK*. Единственное, со знаком ">=" всё-таки надо строгое ограничение (без =), потому что если одна услуга заканчивается '2008-09-18 12:00' а другая начинается в это же время - это не пересечение

kim-aa 19-09-2008 11:02 903136

Цитата:

Цитата pva
Временные - это в том смысле, чтобы они уничтожались после выхода из блока, где были созданы, независимо от того успешно прошла операция или нет »

Можно, но права администратора БД-нужны, (что не правильно в приложении).

View, с точки зрения программиста - SQL-запрос/макрос хранимый на стороне сервера. С точки зрения видимости - таблица БД.
Т. е. этакая виртуальная динамическая таблица.
Т. к. View практически ничего не занимают, то смысла Создавать-Удалять их в процессе выполнения нет никакого.
Более того, с точки зрения структурирования и изоляции кода это глупость.

Хочу заметить, что связка нескольких View в одном запросе читабельнее чем один сложный запрос с join.

Если вы хотите использовать конструкции управляемые в процессе выполнения, обратите внимание на хранимые процедуры.

pva 20-09-2008 20:48 904287

Цитата:

Цитата kim-aa
Хочу заметить, что связка нескольких View в одном запросе читабельнее чем один сложный запрос с join. »

Поэтому и подумываю как бы тут view применить
Цитата:

Цитата kim-aa
Если вы хотите использовать конструкции управляемые в процессе выполнения, обратите внимание на хранимые процедуры. »

Рассматриваем только данный конкретный случай: нужно сказать серверу что я собираюсь сделать 2 раза одинаковый запрос и соединить его. Оставлять view навека в базе не хочется. Всё-таки временную таблицу (declare @t1 table...) сделать?

kim-aa 22-09-2008 11:16 905494

Цитата:

Цитата pva
Оставлять view навека в базе не хочется »

А почему собственно? Они для этого и предназначены.

Цитата:

Цитата pva
Всё-таки временную таблицу (declare @t1 table...) сделать? »

Временная таблица, ( т. е. та которая существует только во время сессии - я правильно вспомнил терминологию MS SQL? Давно не работал с ним.)
Тоже не плохой выход, особенно с точки зрения, работы с высоконагруженными базами.
Наверно, самый оптимальный случай особенно при очень сложных выборках.

С точки зрения блокировок, "монолитный" запрос это самое печальное.

Busla 22-09-2008 14:03 905617

Цитата:

Цитата kim-aa
Временная таблица, ( т. е. та которая существует только во время сессии - я правильно вспомнил терминологию MS SQL? »

на самом деле pva путает понятия временная локальная таблица и таблица-переменная. Он использует таблицу-переменную. Суть, впрочем, та же - автоматически удаляется с закрытием сессии.

Цитата:

Цитата kim-aa
Тоже не плохой выход, особенно с точки зрения, работы с высоконагруженными базами. »

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

kim-aa 22-09-2008 18:37 905875

Цитата:

Цитата Busla
но временную таблицу ещё надо где-то хранить и строить к ней свои индексы. »

"Быстро, качественно, недорого. Выберите любые два пункта!"
Естественно придется жертвовать памятью ради скорости, либо наоборот.

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

Однако, повторюсь:
1) Лично я бы работал с View.
2) С MS SQL не работал давно, за сим мои мысли о проиводительности можно опустить, как ламерские :)

pva 23-09-2008 07:02 906272

Цитата:

Цитата kim-aa
Цитата pva:
Оставлять view навека в базе не хочется »
А почему собственно? Они для этого и предназначены. »

Потому что view типа "показать данные за 4 недели" несёт смысловую нагрузку только для данного конкретного запроса, который выполняется раз в месяц. Нужен он только для того, чтобы сказать серверу, что я собираюсь сделать 2 раза одинаковую выборку. Если бы это была наполненная смыслом выборка хотя бы для нескольких отчётов, я бы сделал view без вопросов.
Цитата:

Цитата Busla
на самом деле pva путает понятия временная локальная таблица и таблица-переменная »

каюсь! мне нужно то, (не знаю как оно правильно называется) что самоуничтожится как автоматическая переменная в с++

kim-aa 23-09-2008 10:40 906386

pva,

Может быть имеет смысл применить функции пользователя ?

Будете передавать в нее две переменных (временной диапазон), получать таблицу.

pva 23-09-2008 15:34 906678

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

Busla 23-09-2008 20:58 906960

pva, это уже - маленький простой запрос - его можно слегка переформулировать, разбить на части, усложнить. Но MS SQL это не тупой интерпретатор наподобие MySQL - простой запрос он сам выполнит оптимальным образом основываясь на текущей ситуации. Промежуточные таблицы, подпрограммы и т.п. только ухудшат его работу и читаемость.

pva 25-09-2008 15:11 908532

Занялся народным творчеством дабы улучшить жизнь любимым девушкам в любимой конторе. Родил кучу вопросов. Продолжаем:
1. Есть ли стандартная функция в SQL, которая выбираем минимум или максимум из 2-х значений
2. Можно ли как-то сделать временную функцию (для экономии кода) вида:
Код:

-- ограничить и превратить во float
CREATE function dbo.fmin(@date_a datetime, @date_b datetime) returns float   
as
begin
  declare @a float, @b float;
  select @a = convert(float, @date_a), @b = convert(float, @date_b);
  return case when @a<@b then @a else @b end;
end

на данный момент я сделал в базе функцию
Код:

CREATE function dbo.fmin(@a float, @b float) returns float   
as return case when @a<@b then @a else @b end;

и в запросе стоит 8 раз convert(float, ...) потому что типа неявные преобразования datetime в float не разрешены

Busla 28-09-2008 13:30 910825

а зачем datetime во float переводить? - это немножко хак, без которого вполне можно обойтись.

select предназначен немножко для другого, просто присвоить переменной значение можно через set:
Код:

set @a = convert(float, @date_a), @b = convert(float, @date_b)
временных функций в T-SQL тоже нету - зачем? Если она нужна - она не должна быть временной; если используется только однажды - красивости можно не наводить. На самом деле, чтобы не "замусоривать" БД - у всех объектов есть владелец и область видимости. Т.е. при правильном проектировании обычный пользователь и не увидит функции и процедуры администратора.

pva 16-10-2008 09:15 925238

Продолжаем тему про временный view. У меня одна процедурка время от времени блокировала работу всегод отдела. Нужно выбрать последнее по дате значение какого-то параметра из сложного запроса, сделано типа так:
Код:

select top 1  /*запрос*/
order by d.date desc

Опытным путём выяснил причину: вот если убрать order by, срабатывает моментом и никого не вешает. В результате переделал так (и всё работает замечательно):
Код:

select top 1 /*запрос*/
where d.date=(select max(d.date) from /*этот же запрос*/)

1. можно как-то сделать так, чтобы 2 раза не писать один запрос (типа "сделать временный view")
2. есть ли альтернативные способы выбрать из таблицы элемент с максимальным значением, без такой блокировки других пользователей

kim-aa 16-10-2008 10:18 925279

Цитата:

Цитата pva
select top 1 /*запрос*/
order by d.date desc »

Мда.
Задача: Вывести строку в которой d.date максимальна (последнее по дате)
Решение: Вывести ВСЮ таблицу (естественно создается какая-то внутренняя системная буферная переменная), отсортировать (в данном буфере), и взять первое значение.

А если в таблице миллионы строк?

Не индус писал?

Кстати поле d.date - индексированно?
====

Цитата:

Цитата pva
2. есть ли альтернативные способы выбрать из таблицы элемент с максимальным значением, без такой блокировки других пользователей »

Для рекомендаций нужно знать как структуру запроса так и таблиц откуда оно берется.

pva 16-10-2008 12:07 925359

Временной таблицы не было, стоял большой where, который я переделал в join и переименовал однобуквенные переменные в многобуквенные. Дата проиндексирована (по возрастанию)
Код:

select top 1
    @last_measure_id = order_.measure_id,
    @last_rate = order_.rate 
 from
    order_
        join doc_ doc2 on (order_.doc_id=doc2.doc_id)
        join agent_ on (agent_.agent_id=doc2.d_agent_id and agent_.agent_type in (0,2))
        join doc_ doc1 on (doc2.other=doc1.other and doc2.date<=doc1.date and doc2.handled=1)
 where
    order_.original_id = @p_id
    and doc2.date = (
        select
            max(doc2.date)
        from
            order_
                join doc_ doc2 on (order_.doc_id=doc2.doc_id)
                join agent_ on (agent_.agent_id=doc2.d_agent_id and agent_.agent_type in (0,2))
                join doc_ doc1 on (doc2.other=doc1.other and doc2.date<=doc1.date and doc2.handled=1)
        where
            order_.original_id = @p_id);



Время: 14:35.

Время: 14:35.
© OSzone.net 2001-