Split space separated values - sql

I have many records like this
ID Tag Arr
1 A 87 34 92
2 A 34 35 38
3 A 39 88 92
4 B 24 49 39
5 B 38 88 23
6 C 39 37 99
I want the end result to look like this in a fast way
Tag Arr No
A 87 1
A 34 2
A 92 2
A 35 1
A 38 1
A 39 1
A 88 1
B 24 1
B 49 1
B 39 1
B 38 1
B 88 1
B 23 1
C 39 1
C 37 1
C 99 1
This is the query i built so far
SELECT DISTINCT T2.tag,
SUBSTRING(
(
SELECT ','+T1.Arr AS [text()]
FROM Tags T1
WHERE T1.tag = T2.tag
and filename = 1
and tag not in ('U')
ORDER BY T1.tag
FOR XML PATH ('')
), 2, 1000) [Ts]
FROM Tags T2
WHERE filename = 1
and tag not in ('U')

You can try this using STRING_SPLIT (Transact-SQL)
which works for SQL Server 2016 and later.
create table tblSampleValue(Id int, Tag Varchar(10), Arr Varchar(50))
insert into tblSampleValue Values
(1, 'A', '87 34 92'),
(2, 'A', '34 35 38'),
(3, 'A', '39 88 92'),
(4, 'B', '24 49 39'),
(5, 'B', '38 88 23'),
(6, 'C', '39 37 99')
Select * from tblSampleValue
select id
, Tag
, [value] as Arr
from tblSampleValue
cross apply string_split(Arr,' ')
Live db<>fiddle demo.

First, you need to use Split function to split Arr column with space.
CREATE FUNCTION udf_Split
( #Words nvarchar(MAX)
, #splitStr varchar(50)
)
RETURNS #Result_Table TABLE
(
[word] nvarchar(max) NULL
)
BEGIN
Declare #TempStr nvarchar(MAX)
WHILE (CHARINDEX(#splitStr,#Words)>0)
BEGIN
Set #TempStr=SUBSTRING(#Words,1,CHARINDEX(#splitStr,#Words)-1)
Insert into #Result_Table (word) Values (#TempStr)
Set #Words = REPLACE(#Words,#TempStr+#splitStr,'')
END/*End While*/
IF(LEN(RTRIM(LTRIM(#Words)))>0 And CHARINDEX(#splitStr,RTRIM(LTRIM(#Words)))=0)
Begin
Set #TempStr=#Words
Insert into #Result_Table (word) Values (#TempStr)
End /*End IF*/
RETURN
END
then use CROSS APPLY with Arr column
SELECT Tag,
word,
count(*) [no]
FROM Tags CROSS APPLY udf_Split(Arr,' ') v
GROUP BY Tag,
word
ORDER BY Tag
sqlfiddle
Note:
if your SQL server version higher than 2016 there is an official function you can use STRING_SPLIT

Related

Calculate data only for special rows [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have table:
ID count OperationID
100 111 1
100 55 2
100 55 3
100 66 4
99 69 1
99 33 2
99 11 14
98 33 1
98 47 2
It is necessary to show data for those 'OperationID' that are in each 'ID'
Ex. output:
ID count OperationID
100 111 1
100 55 2
99 69 1
99 33 2
98 33 1
98 47 2
Maybe join table with itself help?But it does not work ...
You can use it.
DECLARE #MyTable TABLE ( ID INT, [count] INT,OperationID INT)
INSERT INTO #MyTable
VALUES
(100,111,1),
(100,55,2),
(100,55,3),
(100,66,4 ),
(99,69,1),
(99,33,2),
(99,11,14),
(98,33,1),
(98,47,2)
SELECT *
FROM #MyTable
WHERE
OperationID IN
(SELECT
OperationID
FROM #MyTable
GROUP BY OperationID
HAVING COUNT(*) = (SELECT COUNT(DISTINCT ID) FROM #MyTable)
)
Result:
ID count OperationID
----------- ----------- -----------
100 111 1
100 55 2
99 69 1
99 33 2
98 33 1
98 47 2
Ok you can try this, but there are some considerations in your dataset, what about if there is a row that does not have a common OpID for example.
declare #tbl table (ID int,Cnt int, OperationID int)
insert into #tbl
select 100,111,1 union
select 100, 55,2 union
select 100, 55,3 union
select 100, 66,4 union
select 99 , 69,1 union
select 99 , 33,2 union
select 99 , 11,14 union
select 98 , 33,1 union
select 98,47,2
select * from #tbl where OperationID in (
select OperationID from #tbl
where id in (
select a.id from
(
select top 1 id, count (distinct OperationID) as opCnt from #tbl
group by id
order by opCnt
) a
)
)
Result
98 33 1
98 47 2
99 33 2
99 69 1
100 55 2
100 111 1
I am sure this is not the most optimal way.
with cte1 as (
select distinct id
from Operations
),
cte2 as (
select distinct operationID
from Operations
)
, cte3 as (
select *
from cte1 cross join cte2
)
SELECT *
FROM operations
WHERE operationid NOT IN(
SELECT cte3.operationid
FROM operations a
right JOIN cte3 ON cte3.id = a.id
AND cte3.operationid = a.operationid
WHERE a.id IS NULL)
You can try it
DECLARE #table TABLE ( id INT, cnt INT, operationid INT )
INSERT INTO #table VALUES
(100,111 , 1 )
,(100, 55 , 2 )
,(100, 55 , 3 )
,(100, 66 , 4 )
,(99 , 69 , 1 )
,(99 , 33 , 2 )
,(99 , 11 , 14)
,(98 , 33 , 1 )
,(98 , 47 , 2 )
;WITH cte
AS (
SELECT
Count(distinct id) TotalId
FROM
#table
)
,cte1 as
(select
operationid
,Count( distinct id) operationid_By_Id
FROM
#table
Group by
operationid )
SELECT * From
#table c
WHERE EXISTS (
SELECT 1 FROM (
SELECT
*
FROM cte1 A
WHERE EXISTS (SELECT 1 FROM cte b WHERE a.operationid_By_Id = b.TotalId)
) d where d.operationid = c.operationid)
Order by id desc , operationid

Grouping by and getting multiple rows to one cell separated by commas

I know that title is ambigious and I know that there are similiar questions here. I looked and could not made anything work exactly as I need.
I need to Group By ReportNumber and those sharing one category add to one cell.
My DB:
ID | CategorySymbol | ReportNumber | NumberInCategory
1 A 31 101
2 B 31 107
3 C 31 121
4 A 32 191
5 A 33 165
6 B 32 156
7 C 32 127
8 A 31 166
9 B 31 177
Desired result:
ReporNumber | CategoryA | CategoryB | CategoryC
31 **101,166** **107,177** 121
32 191 156 127
33 165 NULL NULL
One of many different attempts:
select ReportNumber,
CategoryFirst, CategorySecond, CategoryThird, CategoryFourth
from
(
select NumberInCategory, Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber,
'Category' + cast(CategorySymbol as varchar(10)) CategorySymbol
from dbo.ReportDB t
) d
pivot
(
max(NumberInCategory)
for CategorySymbol in (CategoryFirst, CategorySecond, CategoryThird, CategoryFourth)
) piv;
My result:
ReporNumber | CategoryA | CategoryB | CategoryC
31 **166** **177** 121
32 191 156 127
33 165 NULL NULL
It is obvious why the result is how it is - max(NumberInCategory). The only question is how do I get to make a query that selects Number based on the CategorySymbol in for loop. I tried doing funtions that return one result like STUFF or simple SELECT but couldn't do them properly. It does not let me replace max(NumberInCategory).
E.g. something like that:
STUFF((SELECT ', ' + CAST(NumberInCategory AS VARCHAR(10)) [text()], Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber
FROM ReportDB
WHERE ReportNumber = t.ReportNumber
FOR XML PATH(''), TYPE)
.value('.','NVARCHAR(MAX)'),1,2,' ') List_Output
Using the stuff() with select ... for xml path ('') method of string concatenation before pivot():
;with t as (
select
NumberInCategory
, Report.value('(/Report/#ReportNumber)[1]', 'nvarchar(max)') AS ReportNumber
, CategorySymbol
from dbo.ReportDB
)
select ReportNumber, CategoryA, CategoryB, CategoryC
from (
select
t.ReportNumber
, CategorySymbol = 'Category'+convert(varchar(10),t.CategorySymbol)
, NumberInCategory = stuff((
select ', '+convert(varchar(13),i.NumberInCategory)
from t i
where i.ReportNumber = t.ReportNumber
and i.CategorySymbol = t.CategorySymbol
order by i.NumberInCategory
for xml path (''), type).value('(./text())[1]','nvarchar(max)')
,1,2,'')
from t
group by t.ReportNumber, t.CategorySymbol
) s
pivot (max(NumberInCategory)
for CategorySymbol in (CategoryA, CategoryB, CategoryC)
) piv;
rextester demo: http://rextester.com/OSAZ69656
returns:
+--------------+-----------+-----------+-----------+
| ReportNumber | CategoryA | CategoryB | CategoryC |
+--------------+-----------+-----------+-----------+
| 31 | 101, 166 | 107, 177 | 121 |
| 32 | 191 | 156 | 127 |
| 33 | 165 | NULL | NULL |
+--------------+-----------+-----------+-----------+
Here's a slightly different flavor...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
ID INT NOT NULL PRIMARY KEY CLUSTERED,
CategorySymbol CHAR(1) NOT NULL,
ReportNumber int NOT NULL,
NumberInCategory INT NOT NULL
);
INSERT #TestData(ID, CategorySymbol, ReportNumber, NumberInCategory) VALUES
(1, 'A', 31, 101),
(2, 'B', 31, 107),
(3, 'C', 31, 121),
(4, 'A', 32, 191),
(5, 'A', 33, 165),
(6, 'B', 32, 156),
(7, 'C', 32, 127),
(8, 'A', 31, 166),
(9, 'B', 31, 177);
-- SELECT * FROM #TestData td;
--==================================================================================
SELECT
td1.ReportNumber,
CategoryA = MAX(CASE WHEN td1.CategorySymbol = 'A' THEN STUFF(c.CSV, 1, 1, '') END),
CategoryB = MAX(CASE WHEN td1.CategorySymbol = 'B' THEN STUFF(c.CSV, 1, 1, '') END),
CategoryC = MAX(CASE WHEN td1.CategorySymbol = 'C' THEN STUFF(c.CSV, 1, 1, '') END)
FROM
#TestData td1
CROSS APPLY (
(SELECT
CONCAT(',', td2.NumberInCategory)
FROM
#TestData td2
WHERE
td1.CategorySymbol = td2.CategorySymbol
AND td1.ReportNumber = td2.ReportNumber
FOR XML PATH(''))
) c (CSV)
GROUP BY
td1.ReportNumber;
Results...
ReportNumber CategoryA CategoryB CategoryC
------------ ---------- ---------- ----------
31 101,166 107,177 121
32 191 156 127
33 165 NULL NULL

group by comma separated list of Ids in TSQL not working

I am working on creating a stored procedure. I have data in this format
InteractiionId(pk) EmployeeId(fk) Reasons
1 107 66
2 107 68
3 107 66,69
4 98 71
5 98 71
6 98 69,71,68
7 90 68
8 90 69,68
9 90 66,71,68
I need to find for each employee count of reasons like this
EmployeeID ReasonsCount
107 66(2)
107 68(1)
107 69(1)
98 71(3)
98 68(1)
98 69(1)
90 68(3)
90 69(1)
90 66(1)
90 71(1)
I am trying to do it like this:
select IdEmployee, E.FirstName,E.LastName, count(reasons), Reasons FROM Interaction I
LEFT JOIN Employee E
ON I.IdEmployee = E.Id
-- where
-- IdEmployee = 95 OR
-- IdEmployee = 98 OR
-- IdEmployee = 107
group by IdEmployee, E.FirstName,E.LastName, Reasons
but it is not working. Please suggest solution.
Thanks.
;with cte
as
(
select * from #temp t
cross apply
SplitStrings_Numbers(c,',') b
)
select b,cast(item as varchar(10))+' ('+ cast(count(item) as varchar(10))+')' as cnt
from
cte
group by b,item
I have used split strings function to split the values
You could use inline split string like this
DECLARE #SampleData AS TABLE
(
InteractiionId int,
EmployeeId int ,
Reasons varchar(200)
)
INSERT INTO #SampleData
VALUES
(1, 107, '66'),
(2, 107, '68'),
(3, 107, '66,69'),
(4, 98 , '71'),
(5, 98 , '71'),
(6, 98 , '69,71,68'),
(7, 90 , '68'),
(8, 90 , '69,68'),
(9, 90 , '66,71,68')
;WITH temp AS
(
SELECT *,
CAST('<X>' + replace(sd.Reasons, ',','</X><X>') +'</X>' AS XML ) AS XmlValue
FROM #SampleData sd
)
SELECT t.EmployeeId, CONCAT(c.[Value], '(', count(*), ')')
FROM temp t
CROSS APPLY
(
SELECT t.x.value('.', 'varchar(100)') AS Value
FROM t.XmlValue.nodes('X') AS t(x)
) c
GROUP BY t.EmployeeId, c.[Value]
ORDER BY t.EmployeeId desc
Demo link: http://rextester.com/EHL21890
;With cte(InteractiionId, EmployeeId, Reasons)
AS
(
SELECT 1, 107, '66' UNION ALL
SELECT 2, 107, '68' UNION ALL
SELECT 3, 107, '66,69' UNION ALL
SELECT 4, 98 , '71' UNION ALL
SELECT 5, 98 , '71' UNION ALL
SELECT 6, 98 , '69,71,68' UNION ALL
SELECT 7, 90 , '68' UNION ALL
SELECT 8, 90 , '69,68' UNION ALL
SELECT 9, 90 , '66,71,68'
)
,CteCount AS (
SELECT a.InteractiionId
,a.EmployeeId
,Split.a.value('.', 'VARCHAR(100)') AS Reasons
FROM (
SELECT InteractiionId
,EmployeeId
,CAST('<M>' + REPLACE(Reasons, ',', '</M><M>') + '</M>' AS XML) AS Reasons
FROM cte
) AS A
CROSS APPLY Reasons.nodes('/M') AS Split(a)
)
SELECT EmployeeId
,CONCAT(Reasons,'(' ,ReasonCounts,')') AS ReasonsCount
FROM (
SELECT EmployeeId,Reasons,ReasonCounts FROM
(
SELECT DISTINCT EmployeeId,Reasons
,COUNT(Reasons) OVER (PARTITION BY EmployeeId,Reasons ORDER BY EmployeeId
) AS ReasonCounts
FROM CteCount
) Dt
) Final
ORDER BY Final.EmployeeId DESC
OutPut
EmployeeId ReasonsCount
------------------------
107 66(2)
107 68(1)
107 69(1)
98 68(1)
98 69(1)
98 71(3)
90 66(1)
90 68(3)
90 69(1)
90 71(1)

SQL Matrix to array

I am working with T-SQL and I have a table that looks like Matrix (8x8).
My objective is to make that Matrix (table) into array using Pivot ... I have read forums and more stuff that i managed to find but i still can't make a code for it ...
ID Bucket B1 B2 B3 B4
5 1 20 21 45 12
6 2 12 18 19 48
7 3 19 78 40 78
8 4 72 34 12 17
So all I need to do is to make "three dimensional array" from that table, and to save row, column and value ... to be something like this
Row Column Value
1 1 20
1 2 21
1 3 45
1 2 12
etc
etc
etc
4 3 12
4 4 17
Does anyone have any idea how I could write that code in T-SQL?
ps. Reason i'm doing this, is because i want to multiply my matrix with itself. So it's easier to multiply it if i have it in pivot table.
Thank you
Try unpivoting your data :
DECLARE #table TABLE (id INT, Bucket INT, B1 INT, B2 INT, B3 INT, B4 INT)
INSERT INTO #table VALUES
(5,1,20,21,45,12),
(6,2,12,18,19,48),
(7,3,19,78,40,78),
(8,4,72,34,12,17)
SELECT rn AS [ROW],
VALUE AS [ColumnNumber],
orders AS [VALUE]
FROM
(
SELECT ROW_NUMBER () OVER (ORDER BY id) AS rn,id, Bucket, B1 [1], B2 [2], B3 [3], B4 [4]
FROM #table
) AS t
UNPIVOT
(
orders
FOR VALUE IN([1], [2],[3],[4])
) AS pvt
Check this MSDN Doc for more details of PIVOT and UNPIVOT.
Why use PIVOT? You can get the data out with a simple SELECT query, unless I am missing something here?
DECLARE #Matrix TABLE (
Id INT,
Bucket INT,
B1 INT,
B2 INT,
B3 INT,
B4 INT);
INSERT INTO #Matrix VALUES (5, 1, 20, 21, 45, 12);
INSERT INTO #Matrix VALUES (6, 2, 12, 18, 19, 48);
INSERT INTO #Matrix VALUES (7, 3, 19, 78, 40, 78);
INSERT INTO #Matrix VALUES (8, 4, 72, 34, 12, 17);
SELECT
Bucket AS [Row],
1 AS [Column],
B1 AS Value
FROM
#Matrix
UNION ALL
SELECT
Bucket AS [Row],
2 AS [Column],
B2 AS Value
FROM
#Matrix
UNION ALL
SELECT
Bucket AS [Row],
3 AS [Column],
B3 AS Value
FROM
#Matrix
UNION ALL
SELECT
Bucket AS [Row],
4 AS [Column],
B4 AS Value
FROM
#Matrix
ORDER BY
1, 2;
Results:
Row Column Value
1 1 20
1 2 21
1 3 45
1 4 12
2 1 12
2 2 18
2 3 19
2 4 48
3 1 19
3 2 78
3 3 40
3 4 78
4 1 72
4 2 34
4 3 12
4 4 17

Multiple left-outer join results in duplicated rows

I have a big table having Incidents. With this I want to attach values from Incident Attachment and Incident Comments. I have used Left outer join as there may or may not be any comments OR attachment for a Incident.
Now when I use multiple outer join i get duplicated rows.
There is a incidentID value that is common for all the tables
INC1 ATT1 COMMENT1
INC1 ATT1 COMMENT2
INC1 ATT2 COMMENT1
INC1 ATT2 COMMENT2
But actually the output should be:
INC1 ATT1 COMMENT1
INC1 ATT2 COMMENT2
so that the attachment and the comment values are independent when attaching themselves to the incident table.
select inc,att.attname,comment.commenttext
from inc
left outer join att inc.incidentID=att.incidentID
left outer join comment inc.incidentID=comment .incidentID
Is this possible?
One of the things I'm taking away from your responses so far is that, for a given incident, there's no direct relationship between its attachments and its comments.
If I understand that correctly, you want to treat an incident as a simple "container" for attachments and comments, and you just want a listing of each. Which
attachment happens to appear in the same row as a particular comment is incidental to you.
TEST DATA:
SQL> CREATE TABLE inc (incidentid NUMBER, incname VARCHAR2(20));
Table created.
SQL> INSERT INTO inc VALUES (1,'incident 1');
1 row created.
SQL> INSERT INTO inc VALUES (2,'incident 2');
1 row created.
SQL> CREATE TABLE att (att_id NUMBER, incidentid NUMBER, attname VARCHAR2(20));
Table created.
SQL> INSERT INTO att VALUES (101, 1, 'attachment 1');
1 row created.
SQL> INSERT INTO att VALUES (102, 1, 'attachment 2');
1 row created.
SQL> INSERT INTO att VALUES (103, 1, 'attachment 3');
1 row created.
SQL> INSERT INTO att VALUES (104, 1, 'attachment 4');
1 row created.
SQL> INSERT INTO att VALUES (105, 1, 'attachment 5');
1 row created.
SQL> INSERT INTO att VALUES (110, 2, 'attachment A');
1 row created.
SQL> INSERT INTO att VALUES (111, 2, 'attachment B');
1 row created.
SQL> INSERT INTO att VALUES (112, 2, 'attachment C');
1 row created.
SQL> CREATE TABLE comments (comment_id NUMBER, incidentid NUMBER, commenttext VARCHAR2(20));
Table created.
SQL> INSERT INTO comments VALUES (201, 1, 'first comment');
1 row created.
SQL> INSERT INTO comments VALUES (202, 1, 'second comment');
1 row created.
SQL> INSERT INTO comments VALUES (203, 1, 'third comment');
1 row created.
PROPOSED QUERY:
SQL> WITH a AS (
2 SELECT att.incidentid
3 , COUNT(att.att_id) rows_per_incident
4 FROM att
5 GROUP BY att.incidentid
6 UNION ALL
7 SELECT comments.incidentid
8 , COUNT(comments.comment_id) rows_per_incident
9 FROM comments
10 GROUP BY comments.incidentid
11 )
12 , b AS (
13 SELECT inc.incidentid
14 , inc.incname
15 , ROW_NUMBER()
16 OVER (PARTITION BY inc.incidentid ORDER BY NULL) row_num
17 FROM inc
18 , (SELECT ROWNUM multiplier FROM DUAL CONNECT BY LEVEL <= (SELECT MAX(rows_per_incident) FROM a))
19 )
20 , c AS (
21 SELECT att.att_id
22 , att.incidentid
23 , att.attname
24 , ROW_NUMBER()
25 OVER (PARTITION BY att.incidentid ORDER BY att.att_id) att_rn
26 FROM att
27 )
28 , d AS (
29 SELECT comments.comment_id
30 , comments.incidentid
31 , comments.commenttext
32 , ROW_NUMBER()
33 OVER (PARTITION BY comments.incidentid ORDER BY comments.comment_id) comm_rn
34 FROM comments
35 )
36 , e AS (
37 SELECT c.incidentid
38 , c.att_id
39 , c.attname
40 , c.att_rn rn
41 , d.comment_id
42 , d.commenttext
43 FROM c
44 , d
45 WHERE c.incidentid = d.incidentid (+)
46 AND c.att_rn = d.comm_rn (+)
47 UNION ALL
48 SELECT TO_NUMBER(NULL) incidentid
49 , TO_NUMBER(NULL) att_id
50 , NULL attname
51 , d.comm_rn rn
52 , d.comment_id
53 , d.commenttext
54 FROM d
55 WHERE NOT EXISTS (SELECT NULL
56 FROM c
57 WHERE c.incidentid = d.incidentid
58 AND c.att_rn = d.comm_rn)
59 )
60 , f AS (
61 SELECT b.incidentid
62 , b.incname
63 , b.row_num
64 , e.att_id
65 , e.attname
66 , e.comment_id
67 , e.commenttext
68 FROM b
69 LEFT OUTER JOIN e ON b.incidentid = e.incidentid
70 AND b.row_num = e.rn
71 )
72 SELECT f.incidentid
73 , f.incname
74 , f.att_id
75 , f.attname
76 , f.comment_id
77 , f.commenttext
78 FROM f
79 WHERE NOT (f.att_id IS NULL AND f.comment_id IS NULL)
80 ORDER BY f.incidentid
81 , f.row_num
82 ;
INCIDENTID INCNAME ATT_ID ATTNAME COMMENT_ID COMMENTTEXT
---------- -------------------- ---------- -------------------- ---------- --------------------
1 incident 1 101 attachment 1 201 first comment
1 incident 1 102 attachment 2 202 second comment
1 incident 1 103 attachment 3 203 third comment
1 incident 1 104 attachment 4
1 incident 1 105 attachment 5
2 incident 2 110 attachment A
2 incident 2 111 attachment B
2 incident 2 112 attachment C
8 rows selected.
SQL>