поиск в БД

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
shady
Постоялец
Постоялец
 
Сообщения: 461
Зарегистрирован: 09.11.2005 (Ср) 11:03

поиск в БД

Сообщение shady » 11.10.2006 (Ср) 17:44

Здравствуйте. Подскажите пожалуйста, правильно ли я делаю и может кто-то знает лучше решение, поделитесь пожалуйста.
Искать я собираюсь при помощи SQL запроса. Примерно следующим образом:

Код: Выделить всё
txtSrch(0) = "Field1 LIKE "
txtSrch(1) = "Field2 LIKE "
txtSrch(2) = "Field3 LIKE "
StrSQL = "SELECT * FROM Table WHERE "

k = -1

For i = 0 To 2
  If Len(txtSearch(i).Text) > 0 Then
    k = k + 1
    If k = 0 Then
      StrSQL = StrSQL & txtSrch(i) & "'%" & txtSearch(i).Text & "%'"
    Else
      StrSQL = StrSQL & " AND (" & txtSrch(i) & "'%" & txtSearch(i).Text & "%')"
    End If
  End If
Next i


Такой способ хорош когда все поля таблицы одного типа. Если например нужно в запросе будет
Код: Выделить всё
WHERE (ID=123) AND ([Name] LIKE '%tes') AND (Post='Manager')
тогда уже придется делать несколько подобных запросов. В моем же случае параметров для поиска может быть очень много. И все они НЕ ОБЯЗАТЕЛЬНЫ для заполнения.
Прикрепил рисунок для большей наглядности.
Что посоветуете?
Вложения
search.JPG
search.JPG (34.64 Кб) Просмотров: 149

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 11.10.2006 (Ср) 18:01

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

Т.е. если поле текстовое, значит для него применим поиск с маской, если числовое, то нет. Если битовое, то только 1 / 0, и так далее. Посиди, подумай для чего какие варианты допустимы и все получится.

А запрос разумеется составлять нужно динамически, в таком случае. И поля поиска, которые юзер не заполнил, не включать в WHERE.

BasiС
Обычный пользователь
Обычный пользователь
Аватара пользователя
 
Сообщения: 64
Зарегистрирован: 16.02.2006 (Чт) 23:34
Откуда: Koenigsberg

Сообщение BasiС » 11.10.2006 (Ср) 19:19

Может так лучше:

Код: Выделить всё
strSql = "SELECT * FROM Table"  'Сам запрос
strSqlVar = ""                  'Переменная для формирования дополнения к строке запроса

If txtSearch(0).Text <> "" Then strSqlVar = strSqlVar & " AND AccSobstv LIKE '" & txtSearch(0).Text & "'" 'для текстового поля
If IsDate(dtpData(1).Value) Then strSqlVar = strSqlVar & " AND AccDataAdd=#" & dtpData(1).Value & "#"     'для поля с датой
If txtSearch(1).Text <> "" Then strSqlVar = strSqlVar & " AND AccName=" & txtSearch(1).Text               'для числового поля
'и т.д. все поля формы

strSql = IIf(strSqlVar = "", strSql, strSql & " WHERE " & Mid(strSqlVar, 6))

Debug.Print strSql 'Смотрим что получилось =)

HandKot
Бывалый
Бывалый
Аватара пользователя
 
Сообщения: 283
Зарегистрирован: 28.06.2006 (Ср) 13:34
Откуда: Sergiev Posad

Сообщение HandKot » 12.10.2006 (Чт) 10:09

а если использовать параметизированный запрос?

получим что-то типа

Код: Выделить всё
SELECT * FROM Table WHERE (Field1 = ? OR ? IS NULL) AND (Field2 = ? OR ? IS NULL) AND (Field3 LIKE '%'+?+'%' OR ? IS NULL)


проверка IS NULL для того, чтобы отбрасывать не введенные параметры

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 12.10.2006 (Чт) 10:16

Параметрический запрос -- это правильно.
Указывать в нем все возможные поля через OR ... IS NULL -- неправильно.
Динамически генерируемый запрос тоже может быть параметрическим.
Lasciate ogni speranza, voi ch'entrate.

Konst_One
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
Аватара пользователя
 
Сообщения: 3041
Зарегистрирован: 09.04.2004 (Пт) 13:47
Откуда: Химки

Сообщение Konst_One » 12.10.2006 (Чт) 10:42

shady
датамайнинг очень непростая штука, недаром столько крупных компаний работают на этом поприще. есть много подходов к реализации того, что ты хочешь. самый простой и надежный - это делать обработку на клиенте. т.е. вытягивай срез данных на клиент, а уже потом накладывай свои условия. если система серьезная, то подумай над olap/datamining сервером, который можно было бы здесь использовать.

динамический запрос, выполняемый на произвольном наборе данных на сервере SQL - не очень удачная идея, так как , как я понимаю, у тебя недетерминированная таблица, поэтому оптимизатор сервера будет строить не оптимальный план выполнения запросов. на больших таблицах будет просто сильно торомозить.

если таблички маленькие (<1000 записей) , то делай хоть с OR и IS NULL при перечеслении всех полей в условии.

shady
Постоялец
Постоялец
 
Сообщения: 461
Зарегистрирован: 09.11.2005 (Ср) 11:03

Сообщение shady » 12.10.2006 (Чт) 11:43

если система серьезная, то подумай над olap/datamining сервером

Я веду разработку в связке с MS SQL 2005, честно говоря, я не думал, что мой вопрос это такая серьезная тема для размышления и изучения, думал что все гораздо проще. В моем случае, возможно, связка будет происходить от 2 до 5 -ти (пока что, возможно в будущем будет больше) таблиц. Т.е. например, если нужно искать счет, то номер счета. дату создания коды покупателей стоит искать только в таблице счетов (Bills). Если еще будет параметром поиска дата оплаты, то поиск будет в 2х таблицах Bills, PayProd ну и так далее. В связи с чем, придется изучать этот вопрос более детально. Если знаете, посоветуйте пожалуйста литературу по теме.

Konst_One
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
Аватара пользователя
 
Сообщения: 3041
Зарегистрирован: 09.04.2004 (Пт) 13:47
Откуда: Химки

Сообщение Konst_One » 12.10.2006 (Чт) 11:47

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

Konst_One
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
Аватара пользователя
 
Сообщения: 3041
Зарегистрирован: 09.04.2004 (Пт) 13:47
Откуда: Химки

Сообщение Konst_One » 12.10.2006 (Чт) 11:48

http://www.mosha.com/msolap/

очень советую

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 12.10.2006 (Чт) 12:04

Лично я использую динамические запросы для таких выборок.
Код: Выделить всё
ALTER       procedure uspSetProject
@ProjectID int
as
declare @Joins varchar(500)
declare @Existances varchar(700)
declare @Likes varchar(1700)
declare @All nvarchar(4000)
set @Joins = dbo.BuildJoins(@ProjectID, 1)
set @Existances = dbo.BuildExistances(@ProjectID)
set @Likes = dbo.BuildLikes(@ProjectID)
if len(@Existances) > 0 and len(@Likes) > 0 set @Existances = @Existances + ' and '
if len(@Existances) <> 0 or len(@Likes) <> 0 begin
    set @All = 'insert into datProjectOrgs (ProjectID, OrgID) select distinct ' + cast(@ProjectID as varchar(8)) + ', datOrganisations.OrgID ' + @Joins + @Existances + @Likes
    print @all
    exec (@All)
end

---------------------------------------------------------------------------

ALTER         function BuildLikes(@ProjectID int)
returns varchar(2700)
as
begin
declare @DataField varchar(50), @Table varchar(50)
declare @FieldValue varchar(128)
declare @Likes varchar(2700)
declare @ObjectID int
declare @ct smallint

declare Objects cursor fast_forward for
select distinct ObjectID
from    tblProjectRules
inner join tblObjectFields on tblObjectFields.FieldID = tblProjectRules.FieldID
where    (tblObjectFields.CompareType = 1 or
   tblObjectFields.CompareType = 3) and
   ProjectID = @ProjectID
set @Likes = ''

open Objects
fetch next from Objects into @ObjectID
WHILE @@FETCH_STATUS = 0
begin
   set @Likes = @Likes + '('

   declare Fields cursor for
   select  distinct
      DataField,
      [Table],
      FieldValue,
      CompareType
   from    tblProjectRules
   inner join tblObjectFields on tblObjectFields.FieldID = tblProjectRules.FieldID
   where    (tblObjectFields.CompareType = 1 or
      tblObjectFields.CompareType = 3) and
      ProjectID = @ProjectID   and
      ObjectID = @ObjectID

   open Fields
   fetch next from Fields into @DataField, @Table, @FieldValue, @ct
   WHILE @@FETCH_STATUS = 0
   begin
      if @ct = 1
         set @Likes = @Likes + @Table + '.' + @DataField + ' like ' + char(39) + '%' + @FieldValue + '%' + char(39) + ' or '
      if @ct = 3
         set @Likes = @Likes + @Table + '.' + @DataField + ' = ' + @FieldValue + ' or '

      fetch next from Fields into @DataField, @Table, @FieldValue, @ct
   end
   close Fields
   deallocate Fields
   set @Likes = substring(@Likes, 1, len(@Likes) - 3) + ') and '
   fetch next from Objects into @ObjectID
end      
close Objects
deallocate Objects
if len(@Likes)>0 set @Likes = substring(@Likes, 1, len(@Likes) - 4)
return @Likes
end

---------------------------------------------------------------------------------

ALTER     function BuildJoins(@ProjectID int, @SingleID bit)
returns varchar(500)
as
begin
/*
declare @ProjectID int
set @ProjectID =160
*/
declare @Joins varchar(500), @ObjectID int
declare Objects cursor fast_forward for
select distinct ObjectID
from tblProjectRules
inner join tblObjectFields on tblObjectFields.FieldID = tblProjectRules.FieldID
where ProjectID = @ProjectID
if @SingleID = 0
   set @Joins = 'from datOrganisations left join cvOrganisationsList on datOrganisations.OrgID = cvOrganisationsList.OrgID ' + char(10)
else
   set @Joins = 'from datOrganisations ' + char(10)
open Objects
fetch next from Objects into @ObjectID
WHILE @@FETCH_STATUS = 0
begin
   set @Joins = @Joins + case @ObjectID
   when 1 then ''
   when 2 then 'inner join datOrgActivities on datOrgActivities.OrgID = datOrganisations.OrgID ' + char(10)
   when 3 then 'inner join datPhones on datPhones.OrgID = datOrganisations.OrgID ' + char(10)
   when 4 then 'inner join datOrgStaff on datOrgStaff.OrgID = datOrganisations.OrgID ' + char(10)
   end
   fetch next from Objects into @ObjectID
end
close Objects
deallocate Objects
set @Joins = @Joins + 'where '
-- print @Joins
return @Joins
end

--------------------------------------------------------------------------------

ALTER     function BuildExistances(@ProjectID int)
returns varchar(700)
as
begin
/*
declare @ProjectID int
set @ProjectID = 162
*/
declare @Prefix varchar (400)
declare @Existances varchar(700)
declare @Table varchar(50), @FieldID int
declare @Field varchar(50)
declare Existance cursor fast_forward for
select    distinct tblProjectRules.FieldID,
   DataField,
   [Table]
from    tblProjectRules
inner join tblObjectFields on tblObjectFields.FieldID = tblProjectRules.FieldID
where    tblObjectFields.CompareType = 2 and
   ProjectID = @ProjectID
set @Existances = ''
set @prefix = 'exists (select FieldValue from tblProjectRules where ProjectID = ' + cast(@ProjectID as varchar(5)) + ' and FieldID = '
open Existance
fetch next from Existance into @FieldID, @Field, @Table
WHILE @@FETCH_STATUS = 0
begin
   set @Existances = @Existances +   
      @prefix + cast(@FieldID as varchar(5)) +
      ' and FieldValue = ' + @Table + '.' +
      @Field + ') and ' + char(10)
   fetch next from Existance into @FieldID, @Field, @Table
end
close Existance
deallocate Existance
if len(@Existances) > 0
set @Existances = substring(@Existances, 1, len(@Existances)-5)
/*
print @Existances
print len(@Existances )
*/
return @Existances
end
Способ работает так. Во вспомогательную таблицу tblObjectFields заносятся таблицы и их поля, по которым нужно проводить поиск, а так же указывается тип сравнения: 1 - через like, 2 - через exists в подчиненных таблицах основного объекта (в данной реализации основной объект - огранизация, подчиненные - виды деятельности, телефоны и т.п), 3 - прямое сравнение id основного объекта (тебе не нужно). В таблицу tblProjectRules заносятся значения и идентификаторы полей, по которым производится поиск, и идентификатор поиска. В конце вызывается процедура uspSetProject.

В твоем случае в этой процедуре нужно заменить set @Existances = @Existances + ' and ' на set @Existances = @Existances + ' or ' и вместо insert делать сразу select.

В функции BuildLikes заменить set @Likes = substring(@Likes, 1, len(@Likes) - 3) + ') and ' на set @Likes = substring(@Likes, 1, len(@Likes) - 3) + ') or ', в BuildJoins заменить все inner join на left join.

Хотя джойны и exists'ы тебе тоже не понадобятся; судя по условиям, шерстить подчиненные таблицы не надо. Достаточно создать вьюхи, представляющие все документы со всеми полями, по которым нужно проводить поиск. И еще тебе нужно добавить сравнение временных промежутков.

Вообще-то, этот способ более универсален, чем требуется тебе. Он позволяет проводить поиск по поизвольному количеству критериев и их значений. Единственное, что не позволяет, так это выбирать условия поиска. Например, если в качестве критериев указаны пара названий организации и пара видов деятельности, то без указанных выше изменений будет производится поиск типа (название OR название) AND (вид OR вид), а с изменениями - (название OR название) OR (вид OR вид).
Вложения
tables.gif
Структура вспомогательных таблиц
(22.7 Кб) Скачиваний: 19
Лучший способ понять что-то самому — объяснить это другому.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 12.10.2006 (Чт) 12:34

Метод в действии. В базе 120 000 организаций. Имеется некоторая тормознутость, это грабилка SnagIT кушает ресурсы.
Вложения
search.rar
Видео
(137.81 Кб) Скачиваний: 43
Лучший способ понять что-то самому — объяснить это другому.

NashRus
Постоялец
Постоялец
 
Сообщения: 388
Зарегистрирован: 18.03.2006 (Сб) 1:16

Сообщение NashRus » 12.10.2006 (Чт) 12:48

2Antonariy: круто. как неплохой достаточно универсальный вариант.

только по "и" нельзя отбирать условия по одному критерию ?
это для множественных значений.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 12.10.2006 (Чт) 13:03

только по "и" нельзя отбирать условия по одному критерию ?
это для множественных значений.
Эээ... чего? Переформулируй вопрос. С примером. :)
Лучший способ понять что-то самому — объяснить это другому.

NashRus
Постоялец
Постоялец
 
Сообщения: 388
Зарегистрирован: 18.03.2006 (Сб) 1:16

Сообщение NashRus » 12.10.2006 (Чт) 16:00

вид документа может быть приказ и постановление. вот надо отобрать документы где вид документа приказ и постановление.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 12.10.2006 (Чт) 20:24

Тип сравнения 2 (exist) можно было бы приспособить.
Но вообще, черезчур универсальное решение будет проигрывать специализированному. Я бы в подобных ситуациях скорее предпочел бы для каждой БД (а то и для каждой логической группы таблиц) свое решение придумать.
Lasciate ogni speranza, voi ch'entrate.

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 12.10.2006 (Чт) 21:47

Построить две вьюхи с приказами и постановлениями и со всеми необходимыми полями. Просто, но поиск по ним будет не быстрым.
Лучший способ понять что-то самому — объяснить это другому.


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: Google-бот и гости: 47

    TopList  
cron