【问题标题】:Sql Query to retrieve following dataSql 查询以检索以下数据
【发布时间】:2015-01-06 22:55:28
【问题描述】:

我有一张有 3 列的表格。前两列是 ID(主键)和国家/地区。第三列包含一些用冒号分隔的名称。例如:

ID    Country    Names
--------------------------
 1     USA       Mike;Bill
 2     USA       Michael;Lara;Van
 3     Italy     Kobe;Nate;Tim;Manu

我需要编写一个 SQL 查询,该查询将为每个名称生成一个新行。例如,在这种情况下,输出将是

ID    Country    Name
--------------------------
 1    USA        Mike
 1    USA        Bill
 2    USA        Michael
 2    USA        Lara
 2    USA        Van
 3    Italy      Kobe
 3    Italy      Nate
 3    Italy      Tim
 3    Italy      Manu

我该怎么做?我在 t-sql 中找到了一个拆分函数,它可以将字符串拆分为一个字符。但是如何将数据拆分成多行呢?

【问题讨论】:

  • 第三列中分隔值的最大数量是多少?
  • 任意数字 - 超过 20。基本上是一个变量。

标签: sql database string tsql split


【解决方案1】:

任务是将名称拆分为各自的行,然后将这些行放入单个结果集中。

我们可以将此 QA 中的此函数用作拆分函数 (T-SQL: Opposite to string concatenation - how to split string into multiple records),因为它是一个表值函数,我们可以像处理任何其他形式的表格数据一样处理结果集(例如,表格、视图、临时表、表变量等)

CREATE FUNCTION dbo.SplitStr( @sep char(1), @s nvarchar(512) )
RETURNS table
AS
RETURN (

    WITH Pieces(pn, start, stop) AS (
        SELECT 1, 1, CHARINDEX( @sep, @s )
        UNION ALL
        SELECT pn + 1, stop + 1, CHARINDEX( @sep, @s, stop + 1 )
        FROM Pieces
        WHERE stop > 0
    )
    SELECT
        pn,
        SUBSTRING( @s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END ) AS s
    FROM
        Pieces
)

然后我们需要为CountryNames表中的每一行调用这个函数并合并结果。如果我们使用CROSS APPLY,这实际上是一个单行,所以需要的查询是这样的:

SELECT
    CountryNames.ID,
    CountryNames.Country,
    Splat.s
FROM
    CountryNames
    CROSS APPLY
    dbo.SplitStr(';', CountryNames.Names ) As Splat

哒哒!

请注意,如果这是一个高性能应用程序或使用大型表,则在应用程序代码中执行此数据转换可能是有意义的。至少尝试规范化您的数据,这样您就不需要在每次要查找此信息时都运行此代码。

【讨论】:

  • 很好的调用,表值分割/解析函数更适合这个问题。
【解决方案2】:

您可以创建一个新表并使用循环到INSERT

SET NOCOUNT ON
DECLARE @intFlag INT
SET @intFlag = 1
WHILE (@intFlag <= (SELECT MAX(LEN(REPLACE(Names,';',';;')) - LEN(Names)) FROM YourOldTable))
BEGIN

INSERT INTO YourNewTable
SELECT  ID,Country
      , Name = dbo.FN_PARSE([Names],';',@intFlag)
FROM  YourOldTable
WHERE  dbo.FN_PARSE([Names],';',@intFlag) IS NOT NULL

SET @intFlag = @intFlag + 1
END
GO
SET NOCOUNT OFF

您的语法可能因您的拆分/解析功能而异。

【讨论】:

    最近更新 更多