SQL: как получить все отдельные символы в столбце, по всем строкам
Есть ли элегантный способ в SQL Server найти все отдельные символы в одном столбце varchar (50) по всем строкам?
Бонусные баллы, если это можно сделать без курсоров:)
Например, скажем, мои данные содержат 3 строки:
productname
-----------
product1
widget2
nicknack3
Различная инвентаризация символов будет "productwigenka123"
Ответы
Ответ 1
Учитывая, что ваш столбец является varchar, это означает, что он может хранить символы только от кодов от 0 до 255, на любой кодовой странице. Если вы используете только диапазон 32-128 ASCII-кода, вы можете просто увидеть, есть ли у вас один из символов 32-128, один за другим. Следующий запрос делает это, глядя в sys.objects.name:
with cteDigits as (
select 0 as Number
union all select 1 as Number
union all select 2 as Number
union all select 3 as Number
union all select 4 as Number
union all select 5 as Number
union all select 6 as Number
union all select 7 as Number
union all select 8 as Number
union all select 9 as Number)
, cteNumbers as (
select U.Number + T.Number*10 + H.Number*100 as Number
from cteDigits U
cross join cteDigits T
cross join cteDigits H)
, cteChars as (
select CHAR(Number) as Char
from cteNumbers
where Number between 32 and 128)
select cteChars.Char as [*]
from cteChars
cross apply (
select top(1) *
from sys.objects
where CHARINDEX(cteChars.Char, name, 0) > 0) as o
for xml path('');
Ответ 2
Здесь запрос, который возвращает каждый символ как отдельную строку, а также количество вхождений. Предполагая, что ваша таблица называется "Продукты"
WITH ProductChars(aChar, remain) AS (
SELECT LEFT(productName,1), RIGHT(productName, LEN(productName)-1)
FROM Products WHERE LEN(productName)>0
UNION ALL
SELECT LEFT(remain,1), RIGHT(remain, LEN(remain)-1) FROM ProductChars
WHERE LEN(remain)>0
)
SELECT aChar, COUNT(*) FROM ProductChars
GROUP BY aChar
Чтобы объединить их все в одну строку (как указано в вопросе), измените окончательный SELECT
на
SELECT aChar AS [text()] FROM
(SELECT DISTINCT aChar FROM ProductChars) base
FOR XML PATH('')
В приведенном выше примере используется хороший хак, который я нашел здесь, который эмулирует GROUP_CONCAT
из MySQL.
Первый уровень рекурсии разворачивается, так что запрос не возвращает пустые строки в выходном файле.
Ответ 3
Используйте это (должно работать на любой РСУ-совместимой РСУБД):
create table prod as
select x.v from (values('product1'),('widget2'),('nicknack3')) as x(v);
Тестовый запрос:
with a as
(
select v, '' as x, 0 as n from prod
union
select v, substring(v,n+1,1) as x, n+1 as n from a where n < len(v)
)
select v, x, n from a -- where n > 0
order by v, n
Окончательный запрос:
with a as
(
select v, '' as x, 0 as n from prod
union
select v, substring(v,n+1,1) as x, n+1 as n from a where n < len(v)
)
select distinct x from a where n > 0
order by x
Версия Oracle:
with a(v,x,n) as
(
select v, '' as x, 0 as n from prod
union all
select v, substr(v,n+1,1) as x, n+1 as n from a where n < length(v)
)
select distinct x from a where n > 0
Ответ 4
Если у вас есть таблица Numbers или Tally, которая содержит последовательный список целых чисел, вы можете сделать что-то вроде:
Select Distinct '' + Substring(Products.ProductName, N.Value, 1)
From dbo.Numbers As N
Cross Join dbo.Products
Where N.Value <= Len(Products.ProductName)
For Xml Path('')
Если вы используете SQL Server 2005 и последующие, вы можете генерировать таблицу Numbers на лету, используя CTE:
With Numbers As
(
Select Row_Number() Over ( Order By c1.object_id ) As Value
From sys.columns As c1
Cross Join sys.columns As c2
)
Select Distinct '' + Substring(Products.ProductName, N.Value, 1)
From Numbers As N
Cross Join dbo.Products
Where N.Value <= Len(Products.ProductName)
For Xml Path('')