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

Откуда:
Сообщений: 9
К примеру есть таблица t1 с текстовым полем name. К примеру name = "текст1 Функция(текст2 *=) Функция(текст3 *=)". Понятно, что если надо определить есть ли в поле name значение по шаблону "Функция(%*=", то пишем запрос
select name
from t1
where  t1.name like "%Функция(%*=%"

Этот запрос выведет значение этого поля. Возник вопрос, каким образом можно определить количество вхождений значений по шаблону "Функция(%*=". В данном примере количество равно 2. Уточню, что поискпо шаблону "Функция(%*=" находит "Функция(текст2 *=" и "Функция(текст3 *=".
П.С. Только начал изучать Transact-Sql. Server 2005.
8 ноя 12, 18:00    [13441511]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
анонимуз
Guest
Я бы написал функцию, которая считает кол-во вхождений лайка в name (два входных параметра).
Возможно использовал бы patindex, replace, substring
8 ноя 12, 18:11    [13441582]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
like %a%b%
Guest
WalS
В данном примере количество равно 2.

а может 3?

WalS
Уточню, что поискпо шаблону "Функция(%*=" находит "Функция(текст2 *=" и "Функция(текст3 *=".

а еще оно находит
Функция(текст2 *=) Функция(текст3 *=
8 ноя 12, 18:22    [13441670]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
AnaceH
Member

Откуда:
Сообщений: 109
WalS,

Задача №8
8 ноя 12, 18:40    [13441799]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
WalS
Member

Откуда:
Сообщений: 9
Пробую, как в задаче8
USE tempdb;
GO
SET NOCOUNT ON;
GO
IF OBJECT_ID('T') IS NOT NULL DROP TABLE T;
GO
/*Тестовая таблица*/
CREATE TABLE T(NameRpt char(200) NOT NULL, Exprn varchar(max) NULL);
GO
INSERT T(NameRpt,Exprn)
  values ('Функция', 'Функция(1234Функция(56*=78')
GO

DECLARE @S VARCHAR(100), @S1 VARCHAR(100), @S2 VARCHAR(100), @i int;
SET @S='Функция(%*=';

WITH CTE(NameRpt,Q,Exprn,P) AS
(
 SELECT NameRpt, 0, Exprn, CAST(''AS VARCHAR(MAX)) FROM T
 UNION ALL
 SELECT NameRpt, Q+1, STUFF(Exprn,1, PATINDEX('%'+@S1+'%'+@S2+'%',Exprn),''), P+SPACE(PATINDEX('%'+@S1+'%'+@S2+'%',Exprn)-1)+'^' FROM CTE WHERE PATINDEX('%'+@S1+'%'+@S2+'%',Exprn)>0
)
SELECT NameRpt, Q, Exprn FROM CTE WHERE Q=0
UNION ALL
SELECT NameRpt, Q, P FROM CTE WHERE Q=(SELECT MAX(T.Q) FROM CTE T WHERE T.NameRpt=CTE.NameRpt AND T.Q>0)
ORDER BY NameRpt,Q
OPTION (MAXRECURSION 0);

GO

DROP TABLE T;


Но проблема в том, что результат должен быть равен 1, а выводит, что 2, так как в строке
SELECT NameRpt, Q+1, STUFF(Exprn,1, PATINDEX('%'+@S+'%',Exprn),''), P+SPACE(PATINDEX('%'+@S+'%',Exprn)-1)+'^' FROM CTE WHERE PATINDEX('%'+@S+'%',Exprn)>0

PATINDEX('%'+@S+'%',Exprn) - ищет строку '%Функция(%*=%', а надо искать '%Функция(56*=%'. Получается, что '%' не отрабатывает.
9 ноя 12, 12:16    [13445183]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
pegoopik
Member

Откуда: Новосибирск
Сообщений: 54
AnaceH
WalS,

Задача №8


Ни в коем случае!
Во-первых использование рекурсии здесь бред.
Рекурсию можно применять в обходе деревьев, графов. А вот на кой черт оно там, где явно цикл?
Единственный минус цикла в том, что там придётся где-то хранить данные, например во временной таблице. Но не вижу в этом криминала.
Хотя если запихнуть в функцию, то всё ништяк.

To Wals

Возможно для Вашей задачи будет достаточно простого len-len(replace)
Как-то так

declare  @S1 varchar(10), @S2 varchar(10)
set @S1='Функция('
set @S2='*='

select name, 
  (
    select MIN(Cnt) 
    From
    (
      select DATALENGTH(name)-DATALENGTH(REPLACE(name, @S1, ''))/DATALENGTH(@S1) as Cnt
      union all
      select DATALENGTH(name)-DATALENGTH(REPLACE(name, @S2, ''))/DATALENGTH(@S2)
    )T
  ) As Cnt
from t1


Минимум взят потому что вхождение одной строки не гарантирует вхождение другой.
Решение задачи 8 кстати говоря тоже не обезопасит от такой ситуации S1...S2....S2...S1...S1...S2...S2
Оно даст 3 как и моё. Но моё прощё.

Если же нужно именно такие, что S1...S2.....S1...S2......S1...S2
Тогда цикл.

Делаем цикл примерно такой

declare  @S1 varchar(10), @S2 varchar(10), 
  @CurrCnt INT, @CurrPos INT,
  @Str varchar(8000)
  
set @S1='function('
set @S2='*='
set @CurrCnt=0
set @CurrPos=1
set @Str = '...'+@S1+'...'+@S2+'....'+@S2+'...'+@S1+'...'+@S1+'...'+@S2+'...'+@S2+'...'

while charindex(@S1, @Str, @CurrPos) > 0
begin
  set @CurrPos = charindex(@S1, @Str, @CurrPos)
  set @CurrPos = charindex(@S2, @Str, @CurrPos)
  if @CurrPos > 0 set @CurrCnt = @CurrCnt + 1
end

select @Str, @CurrCnt


А далее делаем с ним что хотим, либо Update в созданное для хранения поля, либо insert во времянку, либо загоняем в функцию, чтобы можно было использовать в запросе.
9 ноя 12, 12:19    [13445205]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
pegoopik
Member

Откуда: Новосибирск
Сообщений: 54
WalS,

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

поэтому лучше переписать так:

SELECT name,
  CASE WHEN C1<C2 THEN C1 ELSE C2 END As Cnt
FROM(
  SELECT name,
    (DATALENGTH(name)-DATALENGTH(REPLACE(name, @S1, ''))/DATALENGTH(@S1) AS C1,
    (DATALENGTH(name)-DATALENGTH(REPLACE(name, @S2, ''))/DATALENGTH(@S2) AS C2
  FROM T
)T


или так

SELECT name,
  CASE WHEN C1<C2 THEN C1 ELSE C2 END As Cnt
FROM T CROSS APPLY(
  SELECT
    (DATALENGTH(name)-DATALENGTH(REPLACE(name, @S1, ''))/DATALENGTH(@S1) AS C1,
    (DATALENGTH(name)-DATALENGTH(REPLACE(name, @S2, ''))/DATALENGTH(@S2) AS C2
)T


Что для оптимизатора одно и тоже
Вариант с APPLY позволяет уменьшить количество подзапросов, что в большом селекте может улучшить читаемость

Присмотритесь - это хороший вариант, если конечно подходит(конкретной задачи я не знаю)
9 ноя 12, 12:35    [13445318]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
WalS
Member

Откуда:
Сообщений: 9
pegoopik, спасибо. Я так понял, что вариант с case учитывает количество вхождений Str1 и Str2 вне зависимости от порядка. Мне подходит второй вариант с циклом, так как там как раз учитывается порядок.
9 ноя 12, 13:26    [13445882]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
AnaceH
Member

Откуда:
Сообщений: 109
pegoopik
Во-первых использование рекурсии здесь бред.
бла-бла
Хотя если запихнуть в функцию, то всё ништяк.

Уважаемый, бред - это использовать Multi-statement функцию там, где можно и нужно использовать inline. Вы себе вообще представляете как работает оптимизатор запросов, например?
pegoopik
А вот на кой черт оно там, где явно цикл?

Процедурномыслие - это всерьез и надолго.
WalS
Но проблема в том, что результат должен быть равен 1, а выводит, что 2, так как в строке
SELECT NameRpt, Q+1, STUFF(Exprn,1, PATINDEX('%'+@S+'%',Exprn),''), P+SPACE(PATINDEX('%'+@S+'%',Exprn)-1)+'^' FROM CTE WHERE PATINDEX('%'+@S+'%',Exprn)>0

PATINDEX('%'+@S+'%',Exprn) - ищет строку '%Функция(%*=%', а надо искать '%Функция(56*=%'. Получается, что '%' не отрабатывает.

Во-первых, ваш запрос возвращает 0 из-за @S1 VARCHAR(100), @S2 VARCHAR(100), которые не заданы.
Во-вторых, почему 1? Вы задаете шаблон Функция(%*=, почему должно искать по Функция(56*=? И в первом сообщении у вас %Функция(%*=%. Не понятно в общем.
9 ноя 12, 13:56    [13446220]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
WalS
Member

Откуда:
Сообщений: 9
AnaceH, под символом "%" понимается любой текст. То есть "Функция(%*=" может быть и "Функция(123*=" и "Функция(456*=" и "Функция( здесь любой текст*=".
9 ноя 12, 14:08    [13446363]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
pegoopik
Member

Откуда: Новосибирск
Сообщений: 54
[quot AnaceH]
pegoopik
Во-первых использование рекурсии здесь бред.
Во-вторых, почему 1? Вы задаете шаблон Функция(%*=, почему должно искать по Функция(56*=? И в первом сообщении у вас %Функция(%*=%. Не понятно в общем.

А ничего, что % в операторе LIKE означает любую подстроку?
Вы вообще не поняли задачу и непонятно что советуете.
А по поводу сравнения цикла и рекурсии я приведу тест, но попозже. Хотя для меня преимущество первого очевидны.
(это к вопросу о том кто из нас представляет как работает оптимизатор)
9 ноя 12, 14:14    [13446430]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
AnaceH
Member

Откуда:
Сообщений: 109
WalS,

Считаем вместе на пальцах:
Исходная строка - Функция(1234Функция(56*=78
Паттерн - %Функция(%*=%
Первое вхождение - Функция(1234Функция(56*=, где второй %="1234Функция(56"
Второе вхождение - Функция(56*=, где второй % = "56"
Итого 2 вхождения, а не одно, как бы вам этого сильно не хотелось.
Что хотели, то и получили. Если вам необходимо, чтобы второй процент не содержал "Функция(" - добавьте это явно, что вам мешает? Не знаю какая у вас конкретная задача, но для вашего примера надо использовать в этом случае паттерн Функция([^(][0-9]*= вместо Функция(%*=
Вуаля
USE tempdb;
GO
SET NOCOUNT ON;
GO
IF OBJECT_ID('T') IS NOT NULL DROP TABLE T;
GO
/*Тестовая таблица*/
CREATE TABLE T(ID INT NOT NULL, S VARCHAR(MAX) NOT NULL);
GO
/*Заполнение тестовыми данными*/
INSERT T(ID,S)
          SELECT  1,'Функция(1234Функция(56*=78' ;
GO
DECLARE @S VARCHAR(100);
SET @S='Функция([^(][0-9]*=';
WITH CTE(ID,Q,S,P) AS
(
 SELECT ID, 0, S, CAST(''AS VARCHAR(MAX)) FROM T
 UNION ALL
 SELECT ID, Q+1, STUFF(S,1, PATINDEX('%'+@S+'%',S),''), P+SPACE(PATINDEX('%'+@S+'%',S)-1)+'^' FROM CTE WHERE PATINDEX('%'+@S+'%',S)>0
)
SELECT ID, Q, S FROM CTE WHERE Q=0
UNION ALL
SELECT ID, Q, P FROM CTE WHERE Q=(SELECT MAX(T.Q) FROM CTE T WHERE T.ID=CTE.ID AND T.Q>0)
ORDER BY ID,Q
OPTION (MAXRECURSION 0);
GO
DROP TABLE T;
GO

Кстати, вам на неполноту описания задачи намекал еще like %a%b%.
pegoopik
и непонятно что советуете

Бывает, может со временем и поймете. Хотя, как я уже писал, процедурномыслие - это всерьез и надолго.
9 ноя 12, 15:20    [13447013]     Ответить | Цитировать Сообщить модератору
 Re: Вопрос по Like  [new]
пока
Guest
AnaceH
Итого 2 вхождения, а не одно, как бы вам этого сильно не хотелось.

Вообще-то моё второе как раз находит одно, и автору темы оно подошло(если не за этим в тему писать то зачем вообще?). А ни одно из предложенных вами такого эффекта не добивается(в том числе последнее).

AnaceH
Бывает, может со временем и поймете. Хотя, как я уже писал, процедурномыслие - это всерьез и надолго.

Мне даже переубеждать вас расхотелось. К чему эти оскорбления? Я на личности не переходил. Я сделал тесты, 23 ваших против 4х моих секунд на 2млн записей.
Вы всерьёз считаете, что рекурсия лучше цикла? Отвечать не надо

Ладно, давно на сайте не был, зашёл посмотреть и как и в прошлый раз дико не понравилась атмосфера.

Можете считать, что я сливаю:)
10 ноя 12, 00:42    [13450230]     Ответить | Цитировать Сообщить модератору
Все форумы / Microsoft SQL Server Ответить