SQL Server将值拆分为列

时间:2017-10-11 05:55:47

标签: sql sql-server sql-server-2008 tsql

我的字符串由$$个字符分隔,如下所示:

BOB$$"DOG"$$"BROWN"$$"9"$$"4"$$"Latest"$$$$$$"small.
EVA$$"CAT"$$"BLACK"$$"1"$$"4"$$$$"Mouse"$$"Milk"$$small. 

我想获得使用后的功能:更新/插入select / SSIS映射

表格如下:

Column1  Column2    Column3  Column4  Column5  Column6      Column7    Column8     Column9
BOB       "DOG"     "BROWN"   "9"        "4"    "Lat,est"   NULL       NULL        small.
EVA       "CAT"     "BLACK"   "1"        "4"      NULL      "Mouse"    "Milk"      small. 

我可以使用功能Update / Insert进行9行选择以获得结果吗? 我可以创建两个表;第一个表包含单列,第二个表包含9或10列。

我需要100多行的通用解决方案。以上两行只是示例。

我想继续使用这种类型的代码:

SELECT   [Column0]
        ,LEFT([Column0], CHARINDEX('$$', [Column0]) - 1) AS [name]

FROM    [dbo].[tablename]

6 个答案:

答案 0 :(得分:1)

你可以使用它。

;WITH CTE_1 AS (
    SELECT TXT= TXT +'$$', ID = ROW_NUMBER() OVER(ORDER BY TXT) 
    FROM (VALUES
            ('BOB$$"DOG"$$"BROWN"$$"9"$$"4"$$"Lat,est"$$$$$$small.'),
            ('EVA$$"CAT"$$"BLACK"$$"1"$$"4"$$$$"Mouse"$$"Milk"$$small.')) AS X(TXT)
)
, CTE_2 AS -- It split text to rows
(
    SELECT RIGHT(CTE_1.TXT, LEN(CTE_1.TXT) - CHARINDEX('$$',CTE_1.TXT)-1) TXT , SUBSTRING(CTE_1.TXT, 0, CHARINDEX('$$',CTE_1.TXT)) WORD, CHARINDEX('$$',CTE_1.TXT) AS CI, CTE_1.ID, 1 WID 
    FROM CTE_1
    UNION ALL
    SELECT RIGHT(CTE_2.TXT, LEN(CTE_2.TXT) - CHARINDEX('$$',CTE_2.TXT)-1) TXT , SUBSTRING(CTE_2.TXT, 0, CHARINDEX('$$',CTE_2.TXT)) WORD, CHARINDEX('$$',CTE_2.TXT) AS CI, CTE_2.ID, WID  = WID +1
    FROM CTE_2  WHERE LEN(CTE_2.TXT) > 0
)
SELECT * FROM (SELECT ID, (CASE WHEN WORD='' THEN NULL ELSE WORD END) WORD, WID FROM CTE_2 ) SRC -- It convert rows to columns by using pivot
    PIVOT(MAX(WORD) FOR WID IN ([1],[2],[3],[4],[5],[6],[7],[8],[9])) PVT
OPTION (MAXRECURSION 0)

结果:

ID                   1          2          3          4          5          6          7          8          9
-------------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
1                    BOB        "DOG"      "BROWN"    "9"        "4"        "Lat,est"  NULL       NULL       small.
2                    EVA        "CAT"      "BLACK"    "1"        "4"        NULL       "Mouse"    "Milk"     small.

答案 1 :(得分:1)

你走在正确的轨道上。使用SSIS导入平面文件时,我们通常会将所有内容导入为varchar数据类型列表(称为登台表),然后检查列值以确保数据类型的一致性。 这使我们可以导入所有内容而不会丢失数据,并为清理数据提供了机会。

方法1:对于您的情况,您可以在SSIS数据流任务源中使用$$作为列分隔符。 SSIS不关心标题列的顺序或名称。您可以在标题名称中添加任何内容,例如第1列,第2列等。 在这种方法中,您将数据从文件导入9列表。如果某些行中的列数较少/较多,则此方法将失败。

方法2:您将所有内容作为单列导入到varchar登台表(1)。 从这里开始拆分所有列并插入到9列(NULLable)的另一个varchar登台表(2)中,然后在将数据移动到数据类型列表之前进行数据验证。

可以在方法2中使用的查询

create table staging_tbl_single_row (datarow varchar(max))
insert into staging_tbl_single_row values
('BOB$$"DOG"$$"BROWN"$$"9"$$"4"$$"Latest"$$$$$$"small.'),
('EVA$$"CAT"$$"BLACK"$$"1"$$"4"$$$$"Mouse"$$"Milk"$$small.')

; with cte as 
(
    select 
    row_number() over (order by (select NULL)) as column1,
    replace(datarow,'$$','|') as column2
    from staging_tbl_single_row
    )
--Insert into SomeTable 
select 
    [1],[2],[3],[4],[5],[6],[7],[8],[9]
from 
(
    select 
        t.column1,
        split_values=SUBSTRING( t.column2, t1.N, ISNULL(NULLIF(CHARINDEX('|',t.column2,t1.N),0)-t1.N,8000)),
        r= row_number() over( partition by column1 order by t1.N) 
    from cte t 
        join
        (
            select 
                t.column2,
                1 as N 
            from cte t  
                UNION ALL
            select 
                t.column2,
                t1.N + 1 as N
            from cte t 
                join
                (
                 select 
                    top 8000
                        row_number() over(order by (select NULL)) as N 
                 from 
                    sys.objects s1 
                        cross join 
                   sys.objects s2 
                ) t1 
            on SUBSTRING(t.column2,t1.N,1) = '|'
         ) t1
          on t1.column2=t.column2
)a
pivot
( 
    max(split_values) for r in ([1],[2],[3],[4],[5],[6],[7],[8],[9])
   )p

see working demo

答案 2 :(得分:0)

解决方案不是我的,并显示了一般方法

declare @s varchar(max) ='BOB$$DOG$$BROWN$$9$$4$$Latest$$$$small.
EVA$$CAT$$BLACK$$1$$4$$$$Mouse$$Mil$$small'

select
 t.n.value('b[1]', 'varchar(10)'),
 t.n.value('b[2]', 'varchar(10)'),
 t.n.value('b[3]', 'varchar(10)'),
 t.n.value('b[4]', 'varchar(10)'),
 t.n.value('b[5]', 'varchar(10)'),
 t.n.value('b[6]', 'varchar(10)'),
 t.n.value('b[7]', 'varchar(10)'),
 t.n.value('b[8]', 'varchar(10)') 

from
 (select cast(cast('' as xml).query('sql:variable("@s")') as varchar(max))) a(s) cross apply
 (select cast('<a><b>' + replace(replace(a.s, '$$', '</b><b>'), '.', '</b></a><a><b>') + '</b></a>' as xml)) b(x) cross apply
 b.x.nodes('a') t(n);

答案 3 :(得分:0)

遵循一个简单的C#示例,可以轻松转换为C#转换:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication10
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] MyInput = new string[]{"BOB$$\"DOG\"$$\"BROWN\"$$\"9\"$$\"4\"$$\"Latest\"$$$$ß\"small."
                                         , "EVA$$\"CAT\"$$\"BLACK\"$$\"1\"$$\"4\"$$$$\"Mouse\"$$\"Milk\"small."};
            int InputIndex = 0;

            string[] delimiters = new string[]{"$$"};

            foreach (string si in MyInput)
            {
                string OutCol01 = null, OutCol02 = null, OutCol03 = null, OutCol04 = null, OutCol05 = null
                     , OutCol06 = null, OutCol07 = null, OutCol08 = null, OutCol09 = null, OutCol10 = null;

                int ColIdx = 0;

                InputIndex += 1;

                string[] x = si.Split(delimiters, StringSplitOptions.None);

                foreach (string s in x)
                {
                    ColIdx += 1;

                    switch (ColIdx)
                    {
                        case 1:
                            OutCol01 = s;
                            break;
                        case 2:
                            OutCol02 = s;
                            break;
                        case 3:
                            OutCol03 = s;
                            break;
                        case 4:
                            OutCol04 = s;
                            break;
                        case 5:
                            OutCol05 = s;
                            break;
                        case 6:
                            OutCol06 = s;
                            break;
                        case 7:
                            OutCol07 = s;
                            break;
                        case 8:
                            OutCol08 = s;
                            break;
                        case 9:
                            OutCol09 = s;
                            break;
                        case 10:
                            OutCol10 = s;
                            break;
                        default:
                            break;
                    }
                }
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol01);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol02);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol03);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol04);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol05);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol06);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol07);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol08);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol09);
                Console.WriteLine(InputIndex.ToString() + " - " + OutCol10);
            }
            Console.ReadKey();
        }
    }
}

答案 4 :(得分:0)

如果您有fix 9 Column Data

,也可以尝试
SELECT CASE
           WHEN Column1 = ''
           THEN NULL
           ELSE Column1
       END Column1,
       CASE
           WHEN Column2 = ''
           THEN NULL
           ELSE Column2
       END Column2,
       CASE
           WHEN Column3 = ''
           THEN NULL
           ELSE Column3
       END Column3,
       CASE
           WHEN Column4 = ''
           THEN NULL
           ELSE Column4
       END Column4,
       CASE
           WHEN Column5 = ''
           THEN NULL
           ELSE Column5
       END Column5,
       CASE
           WHEN Column6 = ''
           THEN NULL
           ELSE Column6
       END Column6,
       CASE
           WHEN Column7 = ''
           THEN NULL
           ELSE Column7
       END Column7,
       CASE
           WHEN Column8 = ''
           THEN NULL
           ELSE Column8
       END Column8,
       CASE
           WHEN Column9 = ''
           THEN NULL
           ELSE Column9
       END Column9
FROM
(
    SELECT DISTINCT
           Split.a.value('/M[1]', 'NVARCHAR(MAX)') Column1,
           Split.a.value('/M[2]', 'NVARCHAR(MAX)') Column2,
           Split.a.value('/M[3]', 'NVARCHAR(MAX)') Column3,
           Split.a.value('/M[4]', 'NVARCHAR(MAX)') Column4,
           Split.a.value('/M[5]', 'NVARCHAR(MAX)') Column5,
           Split.a.value('/M[6]', 'NVARCHAR(MAX)') Column6,
           Split.a.value('/M[7]', 'NVARCHAR(MAX)') Column7,
           Split.a.value('/M[8]', 'NVARCHAR(MAX)') Column8,
           Split.a.value('/M[9]', 'NVARCHAR(MAX)') Column9
    FROM
    (
        SELECT CAST('<M>'+REPLACE(<Column>, '$$', '</M><M>')+'</M>' AS XML) AS String
        FROM <table_name>
    ) A
    CROSS APPLY String.nodes('/M') Split(a)
) A;

期望输出:

Column1  Column2    Column3  Column4  Column5  Column6      Column7    Column8     Column9
BOB       "DOG"     "BROWN"   "9"        "4"    "Lat,est"      NULL      NULL       small.
EVA       "CAT"     "BLACK"   "1"        "4"      NULL      "Mouse"     "Milk"     small. 

答案 5 :(得分:0)

虽然您已经接受了另一种解决方案,但我想展示如何解决这个SSIS方式,绝对不需要编码:

1)如果尚未存在,请将数据流元素添加到您的包控制流程中。

2)转到数据流并添加平面文件来源元素。

3)点击编辑平面文件来源。

  • 连接管理器窗格中,选中复选框&#34;将来自源的空值保留为数据流中的空值&#34; < / p>

  • 点击新建... 按钮创建新的平面文件连接。

4)平面文件连接管理器编辑器上,单击浏览... 以打开源文件。

  • 选择所需的代码页/编码

  • 保留格式文本限定符标题行分隔符标题行的预选默认值跳过

  • 取消选中&#34;第一个数据行中的列名称&#34; (除非您的示例以外,您的真实文件包含标题行)

5)切换到窗格并将列分隔符更改为$$(只需输入 - 列出的值只是被提议作为最常用的选择)

  • 点击刷新按钮,查看您的列是否符合预期。

6)切换到高级窗格。

  • 选中左侧列表框中的每一列,并将名称属性编辑为有意义的值

  • 根据需要修改 OutputColumnWidth 的值。

  • 选择左侧列表框中的所有列,并将 TextQualified 质量编辑为False ...这样可确保您的周围报价符合要求。

  • 切换到预览窗格,使用确定检查并关闭平面文件连接编辑器。

7)使用确定关闭平面文件源编辑器。

8)添加所需的目标(OLE DB)并连接您的平面文件源