2017-06-01 36 views
0

多列多行我有一个表如下:字符串分割到在配对

Order No | Customers   | Amount  
---------+----------------------+------------- 
1  | Briant~~Luck   | 23~~2122 
2  | Mike~~Lee~~David  | 10~~200~37 
3  | Stak     | 100 

随着格式,每个客户在Amount一个值。

我试图找出如何扩大~~限定值来填充新的客户表,它应该是这样的:

Order No | Customer    | Amount  
---------+----------------------+--------- 
1  | Briant    | 23 
1  | Luck     | 2122 
2  | Mike     | 10 
2  | Lee     | 200 
2  | David    | 37 
3  | Stak     | 100 

我该怎么办?

赞赏SQL查询,函数或游标的任何解决方案。

感谢

+0

尝试新鲜事物? – Arion

+1

你使用的是什么rdms? (mysql,oracel,mssql) – Arion

+0

plsql赞赏? –

回答

0

如果你是SQL2016 +那就试试这个:

drop table if exists dbo.Orders; 

create table dbo.Orders (
ID int 
, Customers varchar(100) 
, Amount varchar(100) 
); 

insert into dbo.Orders (ID, Customers, Amount) 
values (1,'Briant~~Luck', '23~~2122') 
, (2, 'Mike~~Lee~~David', '10~~200~~37') 
, (3, 'Stak', '100'); 


select 
o.ID 
, t01.value as Customer 
, t02.value as Amount 
from dbo.Orders o 
outer apply (
    select 
     ROW_NUMBER() over (order by o.Customers ASC) as Rbr 
     , t.value 
    from string_split (replace(o.Customers, '~~', '~'), '~') t 
) t01 
outer apply (
    select 
     ROW_NUMBER() over (order by o.Amount ASC) as Rbr 
     , t.value 
    from string_split (replace(o.Amount, '~~', '~'), '~') t 
) t02 
where t01.Rbr = t02.Rbr 
+0

请注意,这需要SQL Server 2016 – iamdave

2

我认为你可以将数据存储为您预期的结果结构。它好多了。
顺便说一句,你可以使用拆分功能,让你的输出

DECLARE @SampleData AS TABLE 
(
    OrderNo int, 
    Customers varchar(200), 
    Amount varchar(200) 
) 

INSERT INTO @SampleData 
(
    OrderNo, 
    Customers, 
    Amount 
) 
VALUES 
(1, 'Briant~~Luck','23~~2122'), 
(2, 'Mike~~Lee~~David','10~~200~~37'), 
(3, 'Stak','100') 


SELECT sd.OrderNo, c.[Value] AS Customer, a.[Value] AS Amount 
FROM @SampleData sd 
CROSS APPLY 
(
    SELECT Pos, Value 
    FROM [dbo].[SplitString](sd.Customers,'~~') 
) c 
CROSS APPLY 
(
    SELECT Pos, Value 
    FROM [dbo].[SplitString](sd.Amount,'~~') 
) a 
WHERE c.Pos = a.Pos 
ORDER BY sd.OrderNo 

拆分功能

CREATE FUNCTION [dbo].[SplitString] (@Text varchar(max),@Delimiter varchar(10)) 
Returns Table 
As 
Return ( 
    Select Pos = Row_Number() over (Order By (Select null)) 
     ,Value = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)'))) 
    From (Select x = Cast('<x>'+ Replace(@Text,@Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i) 
); 

演示链接:http://rextester.com/XRX32958

0

如果你是一个版本的SQL Server的早期到2016年,您将需要创建您自己的字符串拆分功能并在脚本中引用它。我使用的版本如下:

create function [dbo].[StringSplit] 
(
    @str nvarchar(4000) = ' '    -- String to split. 
    ,@delimiter as nvarchar(1) = ','  -- Delimiting value to split on. 
    ,@num as int = null      -- Which value to return. 
) 
returns table 
as 
return 
(      -- Start tally table with 10 rows. 
    with n(n) as (select n from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n(n)) 
         -- Select the same number of rows as characters in @str as incremental row numbers. 
        -- Cross joins increase exponentially to a max possible 10,000 rows to cover largest @str length. 
     ,t(t) as (select top (select len(@str) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4) 
          -- Return the position of every value that follows the specified delimiter. 
     ,s(s) as (select 1 union all select t+1 from t where substring(@str,t,1) = @delimiter) 
          -- Return the start and length of every value, to use in the SUBSTRING function. 
        -- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string. 
     ,l(s,l) as (select s,isnull(nullif(charindex(@delimiter,@str,s),0)-s,4000) from s) 
    select rn as ItemNumber 
      ,Item 
    from(select row_number() over(order by s) as rn 
       ,substring(@str,s,l) as item 
     from l 
     ) a 
    where rn = @num   -- Return a specific value where specified, 
     or @num is null  -- Or the everything where not. 
) 

并且使用如下。请注意,我已在outer apply分成单独的查询,以避免行重复:

declare @t table(OrderNo int,Customers nvarchar(500),Amount nvarchar(500)); 
insert into @t values 
(1,'Briant~~Luck','23~~2122') 
,(2,'Mike~~Lee~~David','10~~200~~37') 
,(3,'Stak','100'); 

with c as 
(
    select t.OrderNo 
      ,c.ItemNumber 
      ,c.Item as Customers 
    from @t t 
     outer apply dbo.StringSplit(replace(t.Customers,'~~','|'),'|',null) c 
),a as 
(
    select t.OrderNo 
      ,a.ItemNumber 
      ,a.Item as Amount 
    from @t t 
     outer apply dbo.StringSplit(replace(t.Amount,'~~','|'),'|',null) a 
) 
select c.OrderNo 
     ,c.Customers 
     ,a.Amount 
from c 
    join a 
     on(c.OrderNo = a.OrderNo 
      and c.ItemNumber = a.ItemNumber 
      ) 
order by a.OrderNo 
     ,c.Customers; 

输出:

+---------+-----------+--------+ 
| OrderNo | Customers | Amount | 
+---------+-----------+--------+ 
|  1 | Briant |  23 | 
|  1 | Luck  | 2122 | 
|  2 | David  |  37 | 
|  2 | Lee  | 200 | 
|  2 | Mike  |  10 | 
|  3 | Stak  | 100 | 
+---------+-----------+--------+ 
0

这里解决方案PLSQL

declare 
type t_array_text is table of varchar2(30); 
type t_array_number is table of number; 

array_text t_array_text := t_array_text(); 
array_number t_array_number := t_array_number(); 
i number := 1; 

cursor c1 is 
    select * from deneme; 

begin 

    FOR c in c1 

    LOOP 

     i := 1; 

     array_text := t_array_text(); 
     array_number := t_array_number(); 


     for rec in (
      SELECT regexp_substr(c.customer, '[[:alpha:]]+', 1, level) a frOM dual 
      CONNECT BY level<=regexp_count(c.customer,'~~')+1) 
     loop 
      array_text.extend(); 
      array_text (i) := rec.a; 
      i := i + 1; 
     end loop; 

     i := 1; 

     for rec in (
      SELECT regexp_substr(c.amount, '[0-9]+', 1, level) a frOM dual 
      CONNECT BY level<=regexp_count(c.amount,'~~')+1) 
     loop 
      array_number.extend(); 
      array_number (i) := rec.a; 
      i := i + 1; 
     end loop; 

     for y in 1..array_text.count loop 
      dbms_output.put_line (c.order_no || ' ' || array_text(y) || ' ' || array_number(y)); 
     end loop; 

    END LOOP; 

end; 

结果如下:

1 Briant 23 
1 Luck 2122 
2 Mike 10 
2 Lee 200 
2 David 37 
3 Stak 10 
0

此解决方案使用XML,CROW APPLY & ROW_NUMBER解构'~~'分隔的字段。

它不需要从SQL Server 2016

-- Using a table variable for the test 
declare @Orders table ([Order No] int, Customers varchar(30), Amount varchar(30)); 
insert into @Orders ([Order No], Customers, Amount) values 
(1,'Briant~~Luck','23~~2122'), 
(2,'Mike~~Lee~~David','10~~200~~37'), 
(3,'Stak','100'); 

SELECT C.[Order No], C.Customer, A.Amount 
FROM 
( 
    SELECT 
    [Order No], 
    row_number() over (partition by [Order No] order by (select 1)) as rn, 
    Customers.Name.value('.', 'VARCHAR(100)') AS Customer 
    FROM (
     SELECT [Order No], CAST ('<x>' + REPLACE(Customers, '~~', '</x><x>') + '</x>' AS XML) AS XCustomers 
     FROM @Orders 
    ) AS Q1 
    CROSS APPLY Q1.XCustomers.nodes ('/x') AS Customers(Name) 
) C 
JOIN (
    SELECT 
    [Order No], 
    row_number() over (partition by [Order No] order by (select 1)) as rn, 
    Amounts.Value.value('.', 'VARCHAR(100)') AS Amount 
    FROM (
     SELECT [Order No], CAST ('<x>' + REPLACE(Amount, '~~', '</x><x>') + '</x>' AS XML) AS XAmounts 
     FROM @Orders 
    ) AS Q1 
    CROSS APPLY Q1.XAmounts.nodes ('/x') AS Amounts(Value) 
) A 
ON (C.[Order No] = A.[Order No] AND C.RN = A.RN); 

一个UDF或STRING_SPLIT功能或者,如果你知道会有这些字符串最多3个值。
然后用PARSENAME招可用于:

SELECT [Order No], 
PARSENAME(REPLACE(Customers, '~~', '.'), v.n) as Customer, 
PARSENAME(REPLACE(Amount, '~~', '.'), v.n) as Amount 
FROM @Orders t 
JOIN (VALUES (1),(2),(3)) AS v(n) 
ON v.n <= (len(Customers) - len(replace(Customers, '~~', ','))+1);