SQL splitfunction reverse - sql

I am trying to make an SQL splitfunction which works reverse. Means
Input: 'Test1,Test2,Test3,Test4'
And the output should be:
| ID | Value |
| 1 | Test4 |
| 2 | Test3 |
| 3 | Test2 |
| 4 | Test1 |
I found a forwardworking function but I don't know what to change to make it work reversed.
I have tried some stuff but it doesn't work.
Here is the original
CREATE FUNCTION [dbo].[SplitString]
(
#InputString VARCHAR(8000),
#Delimiter CHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(StartPos,Endpos)
AS(
SELECT 0 AS StartPos, CHARINDEX(#Delimiter,#InputString) AS Endpos
UNION ALL
SELECT Endpos+1, CHARINDEX(#Delimiter,#InputString,Endpos+1)
FROM Split
WHERE Endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Value' = SUBSTRING(#InputString,StartPos
,COALESCE(NULLIF(Endpos,0)
,LEN(#InputString)+1)-StartPos)
FROM Split
)
GO
with
SELECT ID, Value
FROM dbo.SplitString('Test1,Test2,Test3,Test4', ',');
you will get the output.

Select * from [dbo].[udf-Str-Parse-8K-Rev]('Test1,Test2,Test3,Test4',',')
Returns
RetSeq RetVal
1 Test4
2 Test3
3 Test2
4 Test1
The UDF
CREATE FUNCTION [dbo].[udf-Str-Parse-8K-Rev] (#String varchar(max),#Delimiter varchar(25))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 a,cte1 b,cte1 c,cte1 d) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter)) = #Delimiter),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By A.N Desc)
,RetVal = LTrim(RTrim(Substring(#String, A.N, A.L)))
From cte4 A
);
--Orginal Source http://www.sqlservercentral.com/articles/Tally+Table/72993/
--Select * from [dbo].[udf-Str-Parse-8K-Rev]('Dog,Cat,House,Car',',')
Notice the Order By in the Final Select

you can it also do it with a query like this:
SELECT
cnt.n AS ID
, SUBSTRING_INDEX(SUBSTRING_INDEX('Test1,Test2,Test3,Test4', ',', cnt.n),',',-1) AS `VALUE` FROM
( SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
) cnt
WHERE
cnt.n <= CHARACTER_LENGTH('Test1,Test2,Test3,Test4')
- CHARACTER_LENGTH(REPLACE('Test1,Test2,Test3,Test4',',',''))+1;
sample
MariaDB [(none)]> SELECT
-> cnt.n AS ID
-> , SUBSTRING_INDEX(SUBSTRING_INDEX('Test1,Test2,Test3,Test4', ',', cnt.n),',',-1) AS `VALUE` FROM
-> ( SELECT 1 AS n UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
-> UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
-> ) cnt
-> WHERE
-> cnt.n <= CHARACTER_LENGTH('Test1,Test2,Test3,Test4')
-> - CHARACTER_LENGTH(REPLACE('Test1,Test2,Test3,Test4',',',''))+1;
+----+-------+
| ID | VALUE |
+----+-------+
| 1 | Test1 |
| 2 | Test2 |
| 3 | Test3 |
| 4 | Test4 |
+----+-------+
4 rows in set (0.00 sec)
MariaDB [(none)]>

Related

Query JSON data without node in SQL

I have a column with JSON data in SQL server with the following values:
+----+-------------------------------------------------------------+
| ID | Values |
+----+-------------------------------------------------------------+
| 1 | [{"Name":"Test1","Type":null}] |
| 2 | [{"Name":"Test2","Type":null}] |
| 3 | [{"Name":"Test3","Type":null},{"Name":"Test4","Type":null}] |
| 4 | [{"Name":"Test5","Type":null},{"Name":"Test6","Type":null}] |
+----+-------------------------------------------------------------+
I want to query the above table in SQL and want results as:
+----+---------+
| ID | Values |
+----+---------+
| 1 | Test1 |
| 2 | Test2 |
| 3 | Test3 |
| 3 | Test4 |
| 4 | Test5 |
| 4 | Test6 |
+----+---------+
Since you are 2014, and IF you are open to a Table-Valued Function consider the following.
Tired of extracting strings, I modified a parse function to accept two non-like delimiters. In this case '"Name":"' and '"'
Example
Select A.ID
,[Values]=B.RetVal
From YourTable A
Cross Apply [dbo].[tvf-Str-Extract]([Values],'"Name":"','"') B
Returns
ID Values
1 Test1
2 Test2
3 Test3
3 Test4
4 Test5
4 Test6
The Function If Interested
CREATE FUNCTION [dbo].[tvf-Str-Extract] (#String varchar(max),#Delimiter1 varchar(100),#Delimiter2 varchar(100))
Returns Table
As
Return (
with cte1(N) As (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cte2(N) As (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By (Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A ),
cte3(N) As (Select 1 Union All Select t.N+DataLength(#Delimiter1) From cte2 t Where Substring(#String,t.N,DataLength(#Delimiter1)) = #Delimiter1),
cte4(N,L) As (Select S.N,IsNull(NullIf(CharIndex(#Delimiter1,#String,s.N),0)-S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By N)
,RetPos = N
,RetVal = left(RetVal,charindex(#Delimiter2,RetVal)-1)
From (
Select *,RetVal = Substring(#String, N, L)
From cte4
) A
Where charindex(#Delimiter2,RetVal)>1
)
If you cannot use SQL server 2016 or CLR functions and your json is fairly simple, you can translate it to xml and use SQL Server xml support
;with cte1(id, [values]) as (
select 1, '[{"Name":"Test1"}]' union all
select 2, '[{"Name":"Test2"}]' union all
select 3, '[{"Name":"Test3"},{"Name":"Test4"}]' union all
select 4, '[{"Name":"Test5"},{"Name":"Test6"}]'
), cte2 as (
select
id, cast(replace(replace(replace(replace([values],'[{"','<d '),'"}]','" />'),'"},{"','" />, <d '),'":','=') as xml) as [values]
from cte1
)
select
c.id,
t.c.value('#Name', 'nvarchar(128)') as [Values]
from cte2 as c
cross apply c.[values].nodes('d') as t(c)

delete records if 2 counts match

I have the following table with data
+---------+--------+--------+
| Trans | Status | Step |
+---------+--------+--------+
| ABCDE | Comple | 1
+---------+--------+--------+
| ABCDE | Error | 2
+---------+--------+--------+
| FGHIJ | Comple | 1
+---------+--------+--------+
| FGHIJ | Comple | 2
+---------+--------+--------+
| KLMNO | Comple | 1
+---------+--------+--------+
| KLMNO | Comple | 2
+---------+--------+--------+
I only want to delete the records where the count of trans and status = 'comple' is the same as count of trans
I could probably do a cursor and loop but that is something I don't want to do.
Thinking along the lines of a having count but probably miles off.
Thanks
I just want to delete FGHIJ and KLMNO as I know all the steps completed.
I want to keep abcde as not all steps completed
Is this what you want?
--delete
select *
from yourtable t
where not exists
(
select 1
from yourtable t2
where t1.Trans=t2.Trans
and status<>'Comple'
)
Use it as it is first to make sure this is what you want to remove, then comment out the SELECT and uncomment the delete
DELETE FROM tbl
WHERE Trans IN ( SELECT trans
FROM tbl
GROUP BY Trans
HAVING COUNT(*) = SUM(CASE WHEN status = 'Comple' THEN 1 ELSE 0 END) )
It deletes rows (by filtering on Trans), where the number of rows in a group of trans is equal to the number of rows in this group with the Comple status
http://sqlfiddle.com/#!18/1768b/10
Try this
DECLARE #Data AS TABLE (Trans Varchar(10) , Status Varchar(10) , Step INT )
INSERT INTO #Data
SELECT 'ABCDE', 'Comple' , 1 UNION ALL
SELECT 'ABCDE', 'Error' , 2 UNION ALL
SELECT 'FGHIJ', 'Comple' , 1 UNION ALL
SELECT 'FGHIJ', 'Comple' , 2 UNION ALL
SELECT 'KLMNO', 'Comple' , 1 UNION ALL
SELECT 'KLMNO', 'Comple' , 2
SELECT * FROM #Data
;WITH CTe
AS
(
SELECT * , ROW_NUMBER()OVER(PArtition by Trans , [Status] ORDER BY Trans) AS TwocountsMatch
FROM #Data
)
DELETE FROM CTe WHERE TwocountsMatch=2 AND [Status]='Comple'
SELECT * FROM #Data
Demo Result :http://rextester.com/TYK92230
If I understand you correctly, you want to delete all of a [Trans] rows, if all of those rows [status] are 'Comple'?
DECLARE #Data AS TABLE (Trans Varchar(10) , Status Varchar(10) , Step INT )
INSERT INTO #Data
SELECT 'ABCDE', 'Comple' , 1 UNION ALL
SELECT 'ABCDE', 'Error' , 2 UNION ALL
SELECT 'FGHIJ', 'Comple' , 1 UNION ALL
SELECT 'FGHIJ', 'Comple' , 2 UNION ALL
SELECT 'KLMNO', 'Comple' , 1 UNION ALL
SELECT 'KLMNO', 'Comple' , 2
SELECT * FROM #Data;
WITH
summarised
AS
(
SELECT
* ,
COUNT(*) OVER (PARTITION BY [trans] ) AS count_trans,
COUNT(*) OVER (PARTITION BY [trans], [status]) AS count_trans_status
FROM
#Data
)
DELETE
summarised
WHERE
[status] = 'Comple'
AND count_trans = count_trans_status
;
SELECT * FROM #Data

SQL Grouping by first digit from sets of record

I need your help in SQL
I have a set of records of Cost center ID below.
what I want to do is to segregate/group them by inserting column to distinguish the category.
as you can see all digits start in 7 is belong to the bold digits.
my expected out is on below image also.
You can as the below:
DECLARE #Tbl TABLE (ID INT)
INSERT INTO #Tbl
VALUES
(735121201),
(735120001),
(5442244),
(735141094),
(735141097),
(4008060),
(735117603),
(40100000),
(735142902),
(735151199),
(4010070)
;WITH TableWithRowId
AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY(SELECT NULL)) RowId,
ID
FROM
#Tbl
), TempTable
AS
(
SELECT T.RowId + 1 AS RowId FROM TableWithRowId T
WHERE
LEFT(T.ID, 1) != 7
), ResultTable
AS
(
SELECT
T.RowId ,
T.ID,
DENSE_RANK() OVER (ORDER BY (SELECT TOP 1 A.RowId FROM TempTable A WHERE A.RowId > T.RowId ORDER BY A.RowId)) AS Flag
FROM TableWithRowId T
)
SELECT * FROM ResultTable
Result:
RowId ID Flag
----------- ----------- ----------
1 735121201 1
2 735120001 1
3 5442244 1
4 735141094 2
5 735141097 2
6 4008060 2
7 735117603 3
8 40100000 3
9 735142902 4
10 735151199 4
11 4010070 4
The following query is similer with NEER's
;WITH test_table(CenterID)AS(
SELECT '735121201' UNION ALL
SELECT '735120001' UNION ALL
SELECT '5442244' UNION ALL
SELECT '735141094' UNION ALL
SELECT '735141097' UNION ALL
SELECT '4008060' UNION ALL
SELECT '735117603' UNION ALL
SELECT '40100000' UNION ALL
SELECT '735142902' UNION ALL
SELECT '735151199' UNION ALL
SELECT '4010070'
),t1 AS (
SELECT *,ROW_NUMBER()OVER(ORDER BY(SELECT 1)) AS rn,CASE WHEN LEFT(t.CenterID,1)='7' THEN 1 ELSE 0 END AS isSeven
FROM test_table AS t
),t2 AS(
SELECT t1.*,ROW_NUMBER()OVER(ORDER BY t1.rn) AS toFilter
FROM t1 LEFT JOIN t1 AS pt ON pt.rn=t1.rn-1
WHERE pt.CenterID IS NULL OR (t1.isSeven=1 AND pt.isSeven=0)
)
SELECT t1.CenterID,x.toFilter FROM t1
CROSS APPLY(SELECT TOP 1 t2.toFilter FROM t2 WHERE t2.rn<=t1.rn ORDER BY rn desc) x
CenterID toFilter
--------- --------------------
735121201 1
735120001 1
5442244 1
735141094 2
735141097 2
4008060 2
735117603 3
40100000 3
735142902 4
735151199 4
4010070 4

How to comapre two columns of a table in sql?

In a table there are two columns:
-----------
| A | B |
-----------
| 1 | 5 |
| 2 | 1 |
| 3 | 2 |
| 4 | 1 |
-----------
Want a table where if A=B then
-------------------
|Match | notMatch|
-------------------
| 1 | 5 |
| 2 | 3 |
| Null | 4 |
-------------------
How can i do this?
I tried something which shows the Matched part
select distinct C.A as A from Table c inner join Table d on c.A=d.B
Try this:
;WITH TempTable(A, B) AS(
SELECT 1, 5 UNION ALL
SELECT 2, 1 UNION ALL
SELECT 3, 2 UNION ALL
SELECT 4, 1
)
,CTE(Val) AS(
SELECT A FROM TempTable UNION ALL
SELECT B FROM TempTable
)
,Match AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) > 1
)
,NotMatch AS(
SELECT
Rn = ROW_NUMBER() OVER(ORDER BY Val),
Val
FROM CTE c
GROUP BY Val
HAVING COUNT(Val) = 1
)
SELECT
Match = m.Val,
NotMatch= n.Val
FROM Match m
FULL JOIN NotMatch n
ON n.Rn = m.Rn
Try with EXCEPT, MINUS and INTERSECT Statements.
like this:
SELECT A FROM TABLE1 INTERSECT SELECT B FROM TABLE1;
You might want this:
SELECT DISTINCT
C.A as A
FROM
Table c
LEFT OUTER JOIN
Table d
ON
c.A=d.B
WHERE
d.ID IS NULL
Please Note that I use d.ID as an example because I don't see your schema. An alternate is to explicitly state all d.columns IS NULL in WHERE clause.
Your requirement is kind of - let's call it - interesting. Here is a way to solve it using pivot. Personally I would have chosen a different table structure and another way to select data:
Test data:
DECLARE #t table(A TINYINT, B TINYINT)
INSERT #t values
(1,5),(2,1),
(3,2),(4,1)
Query:
;WITH B AS
(
( SELECT A FROM #t
EXCEPT
SELECT B FROM #t)
UNION ALL
( SELECT B FROM #t
EXCEPT
SELECT A FROM #t)
), A AS
(
SELECT A val
FROM #t
INTERSECT
SELECT B
FROM #t
), combine as
(
SELECT val, 'A' col, row_number() over (order by (select 1)) rn FROM A
UNION ALL
SELECT A, 'B' col, row_number() over (order by (select 1)) rn
FROM B
)
SELECT [A], [B]
FROM combine
PIVOT (MAX(val) FOR [col] IN ([A], [B])) AS pvt
Result:
A B
1 3
2 4
NULL 5

tSQL UNPIVOT of comma concatenated column into multiple rows

I have a table that has a value column. The value could be one value or it could be multiple values separated with a comma:
id | assess_id | question_key | item_value
---+-----------+--------------+-----------
1 | 859 | Cust_A_1 | 1,5
2 | 859 | Cust_B_1 | 2
I need to unpivot the data based on the item_value to look like this:
id | assess_id | question_key | item_value
---+-----------+--------------+-----------
1 | 859 | Cust_A_1 | 1
1 | 859 | Cust_A_1 | 5
2 | 859 | Cust_B_1 | 2
How does one do that in tSQL on SQL Server 2012?
We have a user defined function that we use for stuff like this that we called "split_delimiter":
CREATE FUNCTION [dbo].[split_delimiter](#delimited_string VARCHAR(8000), #delimiter_type CHAR(1))
RETURNS TABLE AS
RETURN
WITH cte10(num) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,cte100(num) AS
(
SELECT 1
FROM cte10 t1, cte10 t2
)
,cte10000(num) AS
(
SELECT 1
FROM cte100 t1, cte100 t2
)
,cte1(num) AS
(
SELECT TOP (ISNULL(DATALENGTH(#delimited_string),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM cte10000
)
,cte2(num) AS
(
SELECT 1
UNION ALL
SELECT t.num+1
FROM cte1 t
WHERE SUBSTRING(#delimited_string,t.num,1) = #delimiter_type
)
,cte3(num,[len]) AS
(
SELECT t.num
,ISNULL(NULLIF(CHARINDEX(#delimiter_type,#delimited_string,t.num),0)-t.num,8000)
FROM cte2 t
)
SELECT delimited_item_num = ROW_NUMBER() OVER(ORDER BY t.num)
,delimited_value = SUBSTRING(#delimited_string, t.num, t.[len])
FROM cte3 t;
GO
It will take a varchar value up to 8000 characters and will return a table with the delimited elements broken into rows. In your example, you'll want to use an outer apply to turn those delimited values into separate rows:
SELECT my_table.id, my_table.assess_id, question_key, my_table.delimited_items.item_value
FROM my_table
OUTER APPLY(
SELECT delimited_value AS item_value
FROM my_database.dbo.split_delimiter(my_table.item_value, ',')
) AS delimited_items