SQL Server STRING_SPLIT -Query is adding more lines rather then allocating serial number in sequence

uemypmqf  于 2023-03-07  发布在  其他
关注(0)|答案(2)|浏览(129)

I am trying to split Serial No of a order item table , which has string separated values. I am able to separate the value but serial no allocation is not happening correctly, Its just showing additional rows.

Table has data like

OrderID OrderItemID Item    Price   SerialNo
100     101         P1      200.50  OW52288-OW52289-OW52290-OW52291-OW52292-OW52293
100     102         P1      200.50  NULL
100     103         P1      100.50  NULL
100     104         P1      300.40  NULL
100     105         P1      600.30  NULL
100     106         P1      300.50  NULL
100     107         P1      500.70  NULL
100     108         P1      200.60  NULL
100 109 P1  800.60  NULL

Result coming

OrderID OrderItemID Item    Price   value
100     101         P1     200.50   OW52288
100     101         P1     200.50   OW52289
100     101         P1     200.50   OW52290
100     101         P1     200.50   OW52291
100     101         P1     200.50   OW52292
100     101         P1     200.50   OW52293

Required result

OrderID OrderItemID Item    Price   SerialNo
100     101         P1      200.5   OW52288
100     102         P1      200.5   OW52289
100     103         P1      100.5   OW52290
100     104         P1      300.4   OW52291
100     105         P1      600.3   OW52292
100     106         P1      300.5   OW52293
100     107         P1      500.7   NULL
100     108         P1      200.6   NULL
100     109         P1      800.6   NULL

--Can please help me

Table & query I am writing

CREATE TABLE dbo.TestOrderItemSerial(
    [OrderID] [int] NOT NULL,
    [OrderItemID] [int] NOT NULL,
    [Item] [nvarchar](50) NULL,
    [Price] [money] NOT NULL,
    [SerialNo] [nvarchar](100) )

Insert into dbo.TestOrderItemSerial values
(100,101,'P1',200.50,'OW52288-OW52289-OW52290-OW52291-OW52292-OW52293')
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price)values
(100,102,'P1',200.50)
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price) values
(100,103,'P1',100.50)
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price) values
(100,104,'P1',300.40)
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price) values
(100,105,'P1',600.30)
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price) values
(100,106,'P1',300.50)
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price) values
(100,107,'P1',500.70)
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price) values
(100,108,'P1',200.60)
Insert into dbo.TestOrderItemSerial (OrderID,OrderItemID,Item,Price) values
(100,109,'P1',800.60)

    

select OrderID,OrderItemID,Item,Price,value 
            FROM dbo.TestOrderItemSerial  with (nolock) 
    CROSS APPLY String_Split(REPLACE(REPLACE((
    CASE WHEN  CHARINDEX('-',SerialNo) = 3 THEN REPLACE (SerialNo,'-','') 
         WHEN  CHARINDEX(' ',SerialNo) = 3 THEN REPLACE (SerialNo,' ','') 
         WHEN  CHARINDEX(' ',SerialNo) = 6 THEN REPLACE (SerialNo,' ','-') 
         ELSE SerialNo END)
        ,',','-'),'/','-'),'-')

Data can be like this also

pw9qyyiw

pw9qyyiw1#

Probably a few ways you can approach this, none particularly pretty as dealing with delimited strings just isn't nice.

You need to split the string into ordered rows that can join to your existing table.

If you were using SQL Server 2022 (16) then string_split would be fine, however for SQL Server 2019 it is not always guaranteed rows will be returned in order, so other methods like using a json array can be used.

Try the following which splits the string(s) and assigns a sequential number on which to join:

with s as (
  select t.OrderId, j.SerialNo, 
    Row_Number() over(partition by Orderid order by t.OrderItemID, j.Seq) seq
  from TestOrderItemSerial t
  cross apply (
    select j.[value] SerialNo, 1 + Convert(tinyint, j.[key]) Seq 
    from OpenJson(Concat('["',replace(Serialno, '-', '","'),'"]')) j
  )j
  where t.SerialNo is not null
), t as (
  select *, Row_Number() over(partition by OrderId order by OrderItemId)Seq
  from TestOrderItemSerial t
)
select t.OrderId, t.OrderItemId, t.Item, t.Price, s.Serialno
from t
Left join s on s.OrderId = t.OrderId and s.Seq = t.Seq
order by OrderId, t.Seq;

Here is a demo fiddle

rqmkfv5c

rqmkfv5c2#

I like using a function for this kind of thing when I can't fix the data (which is the right thing to do). Assuming SQL Server 2016 or better (always good to tell us via a tag on the question, like sql-server-2019 ):

CREATE OR ALTER FUNCTION dbo.SplitOrdered_JSON
(
  @List      varchar(max),
  @Delimiter varchar(10)
)
RETURNS table WITH SCHEMABINDING
AS
  RETURN
  (
    SELECT seq = [key] + 1, value 
    FROM OPENJSON(N'["' + REPLACE(STRING_ESCAPE(@List, 'JSON'), 
    @Delimiter, '","') + '"]') AS x
  );

Now the query is:

WITH sn AS
(
  SELECT t.OrderID, j.seq, j.value
  FROM 
  (
    SELECT t.OrderID, sn = STRING_AGG(t.SerialNo, '-')
      WITHIN GROUP (ORDER BY t.OrderItemID)
    FROM dbo.TestOrderItemSerial AS t
    WHERE t.SerialNo > '' GROUP BY t.OrderID
  ) AS t CROSS APPLY dbo.SplitOrdered_JSON(t.sn, '-') AS j
),
Orders AS
(
  SELECT OrderID, OrderItemID, Item, Price,
    rn = ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY OrderItemID)
  FROM dbo.TestOrderItemSerial
)
SELECT o.OrderID, o.OrderItemID, o.Item, o.Price, sn.value
  FROM Orders AS o 
  LEFT OUTER JOIN sn
  ON o.OrderID = sn.OrderID
  AND o.rn = sn.seq
ORDER BY o.OrderID, o.OrderItemID;

With the new information, sure, we can handle any number of delimiters. Slash, space, dash, pencil... you name it. We can just use TRANSLATE() to convert them all to dashes first:

WITH sn AS
(
  SELECT t.OrderID, j.seq, j.value
  FROM 
  (
    SELECT t.OrderID, sn = STRING_AGG(
      TRANSLATE(t.SerialNo, N'/ ✎',N'---'), N'-')
------^^^^^^^^^--------------- only change
      WITHIN GROUP (ORDER BY t.OrderItemID)
    FROM dbo.TestOrderItemSerial AS t
    WHERE t.SerialNo > '' GROUP BY t.OrderID
  ) 
  AS t CROSS APPLY dbo.SplitOrdered_JSON(t.sn, '-') AS j
),
...

But please, please, please consider storing your data properly in the first place. Seems whoever designed this system forgot about the R in RDBMS - if these serial numbers are independent pieces of data, they should always be stored separately and never shmeared together like goop.

And please state all of your requirements up front. When you post half of the info, people who are volunteering their valuable time might only help you solve half of the problem, but they waste potentially all of their time. See chameleon questions and How do I ask a good database question?

相关问题