SQL:如何跨所有行获取列中的所有不同字符

时间:2010-05-27 23:38:50

标签: sql sql-server string puzzle

在SQL Server中是否有一种优雅的方法可以在所有行中查找单个varchar(50)列中的所有不同字符?

如果没有游标可以完成加分:)

例如,假设我的数据包含3行:

productname
-----------
product1
widget2
nicknack3

不同的字符清单是“productwigenka123”

5 个答案:

答案 0 :(得分:19)

这是一个查询,它将每个字符作为单独的行返回,以及出现的次数。假设您的表名为“产品”

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('')

以上使用了一个很好的黑客,我找到了here,它模仿了MySQL的GROUP_CONCAT

展开第一级递归,以便查询不会在输出中返回空字符串。

答案 1 :(得分:7)

使用它(适用于任何支持CTE的RDBMS):

select x.v into prod from (values('product1'),('widget2'),('nicknack3')) as x(v);

测试查询:

with a as 
(
    select v, '' as x, 0 as n from prod 
    union all
    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
option (maxrecursion 0)

最终查询:

with a as 
(
    select v, '' as x, 0 as n from prod 
    union all
    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
option (maxrecursion 0)

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

答案 2 :(得分:5)

鉴于您的列是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('');

答案 3 :(得分:1)

如果你有一个包含整数顺序列表的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及更高版本,则可以使用CTE动态生成Numbers表:

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('')

答案 4 :(得分:0)

基于mdma的答案,此版本为您提供了一个字符串,但可以解码FOR XML将进行的某些更改,例如&-> &amp;

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 STUFF((
  SELECT N'' + aChar AS [text()]
  FROM (SELECT DISTINCT aChar FROM Chars) base
  ORDER BY aChar
FOR XML PATH, TYPE).value(N'.[1]', N'nvarchar(max)'),1, 1, N'')
-- Allow for a lot of recursion. Set to 0 for infinite recursion
OPTION (MAXRECURSION 365)