Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Microsoft SQL Server Новый топик    Ответить
 Прошу помощи в написании запроса  [new]
Super_cherry
Member

Откуда:
Сообщений: 67
Правильно ли идеологически написан запрос?

SELECT Id FROM dbo.SS_FirstDan WHERE
(AnkId IN (SELECT IdNom FROM Word_Fine WHERE (Kod = 1003001101)
AND ( Contains(Znach, '"мос*"'))))

And (AnkId IN (SELECT IdNom FROM Word_Fine WHERE (Kod = 1003002101)
AND ( Contains(Znach, '"орл*"'))))

And (AnkId IN (SELECT IdNom FROM Word_Fine WHERE (Kod = 1003003101)
AND ( Contains(Znach, '"петр*"'))))

Спасибо.
14 май 12, 21:16    [12553222]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
denis2710
Member

Откуда: Москва
Сообщений: 3384
Super_cherry,
А собственно какая цель стояла перед запросом?
идеологически правильно написанный запрос :
select 1

ЗЫ зачем столько скобок?
15 май 12, 09:04    [12554073]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
Мистер Хенки
Member

Откуда: канализация
Сообщений: 6615
А вы уверены что AnkId может быть одновремнно в множестве
SELECT IdNom FROM Word_Fine WHERE (Kod = 1003001101)
AND ( Contains(Znach, '"мос*"'))


SELECT IdNom FROM Word_Fine WHERE (Kod = 1003002101)
AND ( Contains(Znach, '"орл*"'))


и
SELECT IdNom FROM Word_Fine WHERE (Kod = 1003003101)
AND ( Contains(Znach, '"петр*"'))


Если это так, то запрос в целом написан верно. Если это не так и AnkId может быть в любом из этих трех множестви не обязательно во всех, то надо использовать в условии оператор or вместо and
SELECT Id FROM dbo.SS_FirstDan WHERE
(AnkId IN (SELECT IdNom FROM Word_Fine WHERE (Kod = 1003001101)
AND ( Contains(Znach, '"мос*"'))))

or (AnkId IN (SELECT IdNom FROM Word_Fine WHERE (Kod = 1003002101)
AND ( Contains(Znach, '"орл*"'))))

or (AnkId IN (SELECT IdNom FROM Word_Fine WHERE (Kod = 1003003101)
AND ( Contains(Znach, '"петр*"'))))
15 май 12, 09:26    [12554140]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
Super_cherry
Member

Откуда:
Сообщений: 67
Мистер Хенки,

Да, AnkId именно и должен удовлетворять каждому условию. Собственно задача в следующем:
Есть таблица Word_Fine в которой хранятся значения - поле Znach, для каждого значения есть поле Kod, идентифицирующий тип, и поле IdNom определяющее принадлежность к полю AnkId таблицы SS_FirstDan.
Необходимо выбирать все значения AnkId для которых выполняются пары условий : Kod = 1003001101 AND ( Contains(Znach, '"мос*"')) , Kod = 1003002101 AND ( Contains(Znach, '"орл*"')) и т.д.
Проблема состоит в том, что при большом количестве пар условий время выполнения запроса увеличивается, вот и не могу понять, то-ли это естественная необходимость, то-ли есть другое, более верное решение.
15 май 12, 20:35    [12559409]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
случайно заглянул
Guest
Super_cherry,

Попробуйте вместо IN написать JOIN
16 май 12, 17:08    [12564712]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
RubinDm
Member

Откуда:
Сообщений: 461
Super_cherry
Необходимо выбирать все значения AnkId ...

Но выбираете Вы почему-то Id, а не AnkId... опечатка?
SELECT Id FROM dbo.SS_FirstDan FROM ... 


запрос можно переписать, например, так:
select SSFD.[Id] /* или AnkId ? */ from dbo.SS_FirstDan SSFD
inner join Word_Fine WF1 on SSFD.AnkId = WF1.IdNom and Kod = 1003001101 and contains(Znach, '"мос*"')
inner join Word_Fine WF2 on SSFD.AnkId = WF2.IdNom and Kod = 1003002101 and contains(Znach, '"орл*"')
inner join Word_Fine WF3 on SSFD.AnkId = WF3.IdNom and Kod = 1003003101 and contains(Znach, '"петр*"')

плюс только в том, что мой вариант легче читать.
минус в том, что таблица Word_Fine сканируется три раза (как и в Вашем варианте запроса, план исполнения наверняка не сильно будет отличаться, если вообще будет).

Надо сделать так, чтобы Word_Fine сканировалась 1 раз. Это можно сделать, если количество искомых пар конечное, вменяемое и известно заранее... Например, в Вашем случае таких пар три. Суть задачи сводится к тому, чтобы убедиться, что их действительно три.

select SSFD.[Id] /* или AnkId ? */
from dbo.SS_FirstDan SSFD
join Word_Fine WF on SSFD.AnkId = WF.IdNom
where
( -- нас интересют не все пары, а только перечисленные ниже
 (Kod = 1003001101 and contains(Znach, '"мос*"')) OR
 (Kod = 1003002101 and contains(Znach, '"орл*"')) OR
 (Kod = 1003003101 and contains(Znach, '"петр*"'))
)
group by SSFD.[Id] /* или AnkId ? */ -- будь там хоть сто пар, каждое уникальное значение SSFD.[Id] покажется только 1 раз
having count(WF.IdNom) = 3 -- если там три пары, значит мы нашли все искомые пары, и SSFD.[Id] надо показать


Запрос все равно будет тупить, но уже не так, ибо в моем случае произойдет одно сканирование WF с более сложным фильтром, а в вашем случае было три сканирования (IN) с фильтрами по-проще.

Чтобы стало совсем хорошо, и чтобы пары искались в разы быстрее (при сканировании WF), надо помочь серверу - создать индекс.
Что такое индекс знаете? Если совсем просто - это копия таблицы, отсортированная по указанным Вами полям. Поддерживать индекс никак не надо, только создать. Актуализировать содержание индекса сервер будет сам, и думать об этом вам не придется.

create index IX_KodZnach_inc_IdNum on Word_Fine ( Kod asc, Znach asc) include ( IdNom )


Когда сервер строит план исполнения запроса - он всегда проверяет наличие индексов для задействованных таблиц. Если индексы есть, он выберет те из них (их может быть много), которые наиболее адекватно соответствуют конкретной задаче поиска. Мы ищем по двум полям - Kod и Znach, и именно поэтому мы создали индекс с сортировками именно по этим полям. Кроме того, серверу из таблицы WF гарантированно понадобится поле IdNom. Поэтому мы дописали в индексе следующее: include ( IdNom ). Это означает, что значение IdNom будет находится непосредственно в индексе рядом с самой парой (Kod,Znach). Т.о. сервер, глядя на индекс, поймет, что в нем есть все что нужно, и будет работать с ним, а не с таблицей WF, в которой наверняка будет множество лишних полей. Лишние поля - это плохо, потому что записи прочитываются целиком, а лишнее чтение - это лишняя потеря памяти и времени процессора.

До кучи можно создать еще такой индекс:


create index IX_AnkId on SS_FirstDan ( AnkId )


а первый индекс лучше переписать так:

create index IX_IdNomKodZnach on Word_Fine ( IdNom asc, Kod asc, Znach asc) 


Сначала в запросе идет сравнение on SSFD.AnkId = WF.IdNom, после чего проверяются пары (Kod,Znach), поэтому индекс в таком виде подойдет идеально.

Но строго говоря, с индексами надо понимать, что делаешь. Фактически - это КОПИИ таблицы, которые сервер вынужден обслуживать в прозрачном для Вас режиме. Чем больше индексов всяких и разных, заточенных под конкретные варианты поиска, тем быстрее работает поиск данных. Но за это вы расплачиваетесь временем, которое будет затрачиваться на операции insert. update & delete, которые неявным образом правоцируют актуализацию индексов средствами самого сервера.
18 май 12, 02:01    [12573886]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
Мистер Хенки
Member

Откуда: канализация
Сообщений: 6615
RubinDm,
в этом запросе к сожалению все несколько сложнее. Используется полнотекстовый поиск Contains(Znach, '"мос*"'), так что имхо с индексами так не получится.
Потом, нельзя так категорично утверждать что
автор
Сначала в запросе идет сравнение on SSFD.AnkId = WF.IdNom, после чего проверяются пары (Kod,Znach), поэтому индекс в таком виде подойдет идеально.
Вобщем это оптимизатор решает чего сначала будет идти. Отбросим пока фактор полнотекстового поиска, положим там что-то вроде like 'мос%' . Вероятно всего при наличии соответствующей статистики сервер посмотрел бы селетивность фильтров на Kod,Znach и если она была бы достаточно хорошей то начал бы именно с фильтрации по этому условию, а только потом соединение SSFD и WF. Но в отсутствии информации о данных ничего определенного на счет того как себя поведет стоимостной оптимизатор сказать нельзя. Ну и индексы тоже лучше не строить, пока нет информации о данных. Потом если уж строить индексы и запросы, то хорошую фильтрацию надо проталкивать как можно ближе к началу выполнения запроса - это так, мое имхо.
18 май 12, 09:55    [12574588]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
denis2710
Member

Откуда: Москва
Сообщений: 3384
RubinDm,
select SSFD.[Id] /* или AnkId ? */
from dbo.SS_FirstDan SSFD
join Word_Fine WF on SSFD.AnkId = WF.IdNom
where
( -- нас интересют не все пары, а только перечисленные ниже
 (Kod = 1003001101 and contains(Znach, '"мос*"')) OR
 (Kod = 1003002101 and contains(Znach, '"орл*"')) OR
 (Kod = 1003003101 and contains(Znach, '"петр*"'))
)
group by SSFD.[Id] /* или AnkId ? */ -- будь там хоть сто пар, каждое уникальное значение SSFD.[Id] покажется только 1 раз
having count(WF.IdNom) = 3 -- если там три пары, значит мы нашли все искомые пары, и SSFD.[Id] надо показать

Если возможна ситуация такая,что
по условию (Kod = 1003001101 and contains(Znach, '"мос*"') вернется например 1 строчка
(Kod = 1003002101 and contains(Znach, '"орл*"') 0
(Kod = 1003003101 and contains(Znach, '"петр*"') 2
То этот запрос будет не верно работать,т.к ТС нужно,что бы условие выполнялось для все трех условий сразу.
Предлагаю in махнуть не глядя на exists и построить необходимые индексы.
написать что-то типа этого
select  Id
from    dbo.SS_FirstDan
where   exists ( select 1
                 from   Word_Fine
                 where  Kod = 1003001101
                        and contains ( Znach, '"мос*"' ) )
        and exists ( select 1
                     from   Word_Fine
                     where  Kod = 1003002101
                            and contains ( Znach, '"орл*"' ) )
        and exists ( select 1
                     from   Word_Fine
                     where  Kod = 1003003101
                            and contains ( Znach, '"петр*"' ) )


По поводу поля Znach в индексах,что-то мне кажется странным,по нему уже есть полнотекстовый индекс(поскольку используется contains) и соответственно какой-то уникальный индекс на таблице .Смысл это поле включать в индекс?


RubinDm
create index IX_KodZnach_inc_IdNum on Word_Fine ( Kod asc, Znach asc) include ( IdNom )

Вообще как-то странно выглядит при
...join Word_Fine WF on SSFD.AnkId = WF.IdNom...

RubinDm
Фактически - это КОПИИ таблицы
Прямо всей таблицы?А столбцы для индекса зачем тогда указывать?
18 май 12, 10:24    [12574840]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
Мистер Хенки
Member

Откуда: канализация
Сообщений: 6615
denis2710,
А какая разница именно в данном случае
автор
Предлагаю in махнуть не глядя на exists и построить необходимые индексы.
написать что-то типа этого
между in и exists ?
18 май 12, 10:28    [12574877]     Ответить | Цитировать Сообщить модератору
 Re: Прошу помощи в написании запроса  [new]
denis2710
Member

Откуда: Москва
Сообщений: 3384
Мистер Хенки,
По сути никакой,личные предпочтения :)
18 май 12, 11:49    [12575695]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить