Is it possible to use only sql query no procedural language to get below format output
ID Name
-------------
1 a,b,c
2 x,y,z
3 m,n,l
I want this to print like below
1:a
1:b
1:c
2:x
2:y
2:z
Is this is possible with only my sql query or I have to use UDF ?
Yes you can-
The solution below is for Mysql.
You can use SUBSTRING_INDEX to reverse the process of 'group_concat'.
Here is your sql-
SELECT
id,
SUBSTRING_INDEX(SUBSTRING_INDEX(names, ',', n.d+1), ',', -1) name
FROM
users
INNER JOIN
(SELECT 0 d UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3) n
ON LENGTH(REPLACE(names, ',' , '')) <= LENGTH(names)-n.d
ORDER BY
id,
n.d
;WITH CTE(ID,Name)
AS
(
SELECT 1,'a,b,c' union all
SELECT 2,'x,y,z' union all
SELECT 3,'m,n,l'
)
SELECT ID, Split.a.value('.', 'VARCHAR(100)') AS Data
FROM
(
SELECT ID,
CAST ('<M>' + REPLACE(Name, ',', '</M><M>') + '</M>' AS XML) AS Data
from CTE
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
If you want ouput with ':' separter ,below is the code
;WITH CTE(ID,Name)
AS
(
SELECT 1,'a,b,c' union all
SELECT 2,'x,y,z' union all
SELECT 3,'m,n,l'
)
SELECT CONCAT(CAST(ID AS VARCHAR(5)),' : ', CAST(Data AS VARCHAR(5))) As [OutPut] From
(
SELECT ID, Split.a.value('.', 'VARCHAR(100)') AS Data
FROM
(
SELECT ID,
CAST ('<M>' + REPLACE(Name, ',', '</M><M>') + '</M>' AS XML) AS Data
from CTE
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a)
)Final
In SQL server, you could do it by a query with CROSS APPLY and xml query
DECLARE #SampleData AS TABLE (ID int IDENTITY (1,1), Name varchar(100))
INSERT INTO #SampleData (Name)
VALUES ('a,b,c'), ('x,y,z'), ('m,n,l')
;WITH temp AS
(
SELECT *,
CAST('<x>' + replace(sd.Name, ',', '</x><x>') + '</x>' AS xml) AS xmlText
FROM #SampleData sd
)
SELECT CONCAT(t.ID, ':',v.x.value('.','varchar(50)')) AS Result
FROM temp t
CROSS APPLY
t.xmlText.nodes('/x') AS v(x)
Demo link: http://rextester.com/IHQF16100
Related
i have one table of product_attributes that is below:-
id attr_details
1 {"Manufacturer":"Lennovo","Warranty":"6 months"}
2 {"Manufacturer":"HP","Warranty":"6 months"}
3 {"Manufacturer":"DEll","Warranty":"12 months","Type":"DDR"}
4 {"Size":"36","Color":"Red","Material":"Fabric"}
My attr_details column data stored in json. My expected Output is like below:-
label values
Manufacturer Lennovo,HP,DEll
Warranty 6 months,12 months
Type DDR
Size 36
Color Red
Material Fabric
The attr_details json is not predefined it can be any userdefined input json. So can anyone help me how to achieve this output.
Try this:
SET #sql = NULL;
SELECT GROUP_CONCAT(CONCAT("SELECT '",colname,":' AS 'Label', GROUP_CONCAT(val)
FROM (SELECT JSON_UNQUOTE(JSON_EXTRACT(attr_details,'$.", colname,"')) AS 'val'
FROM mytable /*you can add the WHERE condition in here*/ GROUP BY val) A
GROUP BY Label") SEPARATOR " UNION ")
INTO #sql
FROM
(WITH RECURSIVE data AS (
SELECT attr_details,JSON_VALUE(JSON_KEYS(attr_details), '$[0]') AS colname, 0 AS idx FROM mytable
UNION
SELECT attr_details,JSON_VALUE(JSON_KEYS(attr_details), CONCAT('$[', d.idx + 1, ']'))
AS colname, d.idx + 1 AS idx FROM data AS d
WHERE d.idx < JSON_LENGTH(JSON_KEYS(attr_details)) - 1
) SELECT colname
FROM data
GROUP BY colname) V;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
This is the closest I can get. The WITH RECURSIVE .. part I use to list out all the keys as single row value each. That one I was referring to a query from the comment section in the MariaDB documentation. Then I construct the query using combination of CONCAT + GROUP_CONCAT. Lastly, I used prepared statement to execute the query. Here is the final output of #sql:
SELECT 'Color:' AS 'Label', GROUP_CONCAT(val)
FROM (
SELECT JSON_UNQUOTE(JSON_EXTRACT(attr_details,'$.Color')) AS 'val'
FROM mytable GROUP BY val) A
GROUP BY Label UNION
SELECT 'Manufacturer:' AS 'Label', GROUP_CONCAT(val)
FROM (
SELECT JSON_UNQUOTE(JSON_EXTRACT(attr_details,'$.Manufacturer')) AS 'val'
FROM mytable GROUP BY val) A
GROUP BY Label UNION
SELECT 'Material:' AS 'Label', GROUP_CONCAT(val)
FROM (
SELECT JSON_UNQUOTE(JSON_EXTRACT(attr_details,'$.Material')) AS 'val'
FROM mytable GROUP BY val) A
GROUP BY Label UNION
SELECT 'Size:' AS 'Label', GROUP_CONCAT(val)
FROM (
SELECT JSON_UNQUOTE(JSON_EXTRACT(attr_details,'$.Size')) AS 'val'
FROM mytable GROUP BY val) A
GROUP BY Label UNION
SELECT 'Type:' AS 'Label', GROUP_CONCAT(val)
FROM (
SELECT JSON_UNQUOTE(JSON_EXTRACT(attr_details,'$.Type')) AS 'val'
FROM mytable GROUP BY val) A
GROUP BY Label UNION
SELECT 'Warranty:' AS 'Label', GROUP_CONCAT(val)
FROM (
SELECT JSON_UNQUOTE(JSON_EXTRACT(attr_details,'$.Warranty')) AS 'val'
FROM mytable GROUP BY val) A
GROUP BY Label
Demo fiddle
WITH cte AS (
SELECT DISTINCT jsontable.label
FROM test
CROSS JOIN JSON_TABLE( JSON_KEYS(test.attr_details),
'$[*]' COLUMNS ( label VARCHAR(255) PATH '$' )) jsontable
)
SELECT cte.label, GROUP_CONCAT(DISTINCT JSON_EXTRACT(test.attr_details, CONCAT('$.', cte.label))) `values`
FROM cte
CROSS JOIN test
GROUP BY label;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ab92deb1dc20eef0f090f841ce2c6cd0
We have
[this following data][1]
[1]: https://i.stack.imgur.com/oFSzj.png
Here, we are showing all the possible parent Ids at the column with a specific separator ‘.’
WITH Hierarchy(ChildId, ChildName, ParentId, Parents)
AS
(
SELECT Id, Name, ParentId, CAST('' AS VARCHAR(MAX))
FROM UserType AS FirtGeneration
WHERE ParentId IS NULL
UNION ALL
SELECT NextGeneration.Id, NextGeneration.Name, Parent.ChildId,
CAST(CASE WHEN Parent.Parents = ''
THEN(CAST(NextGeneration.ParentId AS VARCHAR(MAX)))
ELSE(Parent.Parents + '.' + CAST(NextGeneration.ParentId AS VARCHAR(MAX)))
END AS VARCHAR(MAX))
FROM UserType AS NextGeneration
INNER JOIN Hierarchy AS Parent ON NextGeneration.ParentId = Parent.ChildId
)
SELECT *
FROM Hierarchy
OPTION(MAXRECURSION 32767)
[the results give: ][2]
[2]: https://i.stack.imgur.com/AV5Xw.png
Do you know how i could have the equivalent with mysql ?
WITH RECURSIVE descendants AS
(
SELECT id, name, CAST(Name AS CHAR(500)) AS path
FROM usertype
where parent_id is null
UNION ALL
SELECT t.id, t.name, CONCAT(d.path, ',', t.name)
FROM descendants d, usertype t
WHERE t.parent_id = d.id
)
SELECT * FROM descendants ORDER BY path;
Good day i would like to ask if this is possible in MySQL
SELECT id,label,name,age,sex FROM table LIMIT 3
Output
[row1] id,label,name,age,sex
[row2] id,label,name,age,sex
[row3] id,label,name,age,sex
My Output Needed
[row1] id
[row2] label
[row3] name
[row4] age
[row5] sex
[row6] id
[row7] label
[row8] name
[row9] age
[row10] sex
[row11] id
[row12] label
[row13] name
[row14] age
[row15] sex
You can do something like this:
SELECT * FROM
((SELECT id AS id1, 1 AS rownum, 'id' AS colname, id AS Data_value FROM mytable LIMIT 3)
UNION ALL
(SELECT id, 2, 'label', label FROM mytable LIMIT 3)
UNION ALL
(SELECT id, 3, 'name', name FROM mytable LIMIT 3)
UNION ALL
(SELECT id, 4, 'age', age FROM mytable LIMIT 3)
UNION ALL
(SELECT id, 5, 'sex', sex FROM mytable LIMIT 3)) A
ORDER BY id1, rownum
Here's a fiddle: https://www.db-fiddle.com/f/dvg6x1vBg6H5bDNp9VZxQa/4
I've added 3 additional column id AS id1, rownum and colname. The first two additional column is used for ORDER BY at the outer query. If you don't want to see the additional column, you can just type SELECT Data_value FROM ... at the outer query.
You can use group_concat() to aggregate rows by string concatenation. For the LIMIT to work you then need to use a derived table. But you should be careful with a LIMIT without an ORDER BY. As the order of a query result can be random unless an explicit ORDER BY is issued, you may get different results each time you run the query.
SELECT group_concat(id,
'\n',
label,
'\n',
name,
'\n',
age,
'\n',
sex
SEPARATOR '\n')
FROM (SELECT id,
label,
name,
age,
sex
FROM elbat
LIMIT 3) x;
If you just want to concatenate the columns but keep the rows just use concat().
SELECT concat(id,
'\n',
label,
'\n',
name,
'\n',
age,
'\n',
sex)
FROM elbat
LIMIT 3;
yes,you can use union all like below :
SELECT id FROM table LIMIT 3
union all
SELECT label FROM table LIMIT 3
union all
SELECT name FROM table LIMIT 3
union all
SELECT age FROM table LIMIT 3
union all
SELECT sex FROM table LIMIT 3
That what you looking is to Unpivot data. For more info about pivot and unpivot you can check here.
http://archive.oreilly.com/oreillyschool/courses/dba1/dba110.html
Unfortunately there is no easy way to unpivot in mysql.
The below script will work for MySQL 8.0
set #rowNum :=0;
set #string :=(
select group_concat(id,',',label,',',name,',',age,',',sex separator ',')
from (
select id, label, name, age, sex from mytable limit 3
) x
);
with recursive
R1 as ( select #string as items),
R2 as ( select 1 as n
union
select n + 1 from R2, R1
where n <= length(items) - length(replace(items, ',', '')))
select distinct #rowNum := #rowNum+1 as rowNum, substring_index(substring_index(items, ',', n), ',', -1) output from R2, R1;
I am new in SQL basically i required Sum result in separate columns as per different elements.
Please find my table and required result in below image.
Please guide me with regards to SQL query which give me the required result.
You can use conditional aggregation:
select payment,
sum(case when product = 'A' then amount else 0 end) as a,
sum(case when product = 'B' then amount else 0 end) as b,
sum(case when product = 'C' then amount else 0 end) as c,
sum(case when product = 'D' then amount else 0 end) as d,
sum(amount) as total
from t
group by payment;
You can use PIVOT to get the desired result
SELECT * ,
[A] + [B] + [C] + [D] AS Total
FROM ( SELECT Payment, Product, Amount FROM Your_Table) tr
PIVOT( SUM(Amount) FOR Product IN ( [A], [B], [C], [D] ) ) p;
You can use dynamic SQL pivot query if your products are not limited with A,B,C and D
Here is a sample query for SQL Server
DECLARE #products nvarchar(max)
SELECT #products =
STUFF(
(
select distinct ',[' + product + ']'
from Payments
for xml path('')
),
1,1,'')
declare #sql nvarchar(max)
set #sql = '
SELECT
*
FROM (
SELECT
Payment AS '' '',
Product,
Amount
from Payments
) Data
PIVOT (
SUM(Amount)
FOR Product
IN (
' + #products + '
)
) PivotTable'
exec sp_executesql #sql
Below is sample data
IF OBJECT_ID('tempdb..#t')IS NOT NULL
DROp TABLE #t
;with cte (Payment,Product,Amount)
AS
(
SELECT 'Cash','A',1 UNION ALL
SELECT 'Credit','B',2 UNION ALL
SELECT 'Credit','C',3 UNION ALL
SELECT 'Cash','D',5 UNION ALL
SELECT 'Cash','A',6 UNION ALL
SELECT 'Credit','B',23 UNION ALL
SELECT 'Credit','C',7 UNION ALL
SELECT 'Cash','D',11 UNION ALL
SELECT 'Cash','A',12 UNION ALL
SELECT 'Credit','B',14 UNION ALL
SELECT 'Credit','C',16 UNION ALL
SELECT 'Cash','D',26
)
SELECT * INTO #t FROM cte
Using Dynamic Sql
DECLARE #DyColumn Nvarchar(max),
#Sql Nvarchar(max),
#ISNULLDyColumn Nvarchar(max),
#SumCol Nvarchar(max)
SELECT #DyColumn=STUFF((SELECT DISTINCT ', '+QUOTENAME(Product) FROM #t FOR XML PATH ('')),1,1,'')
SELECT #ISNULLDyColumn=STUFF((SELECT DISTINCT ', '+'ISNULL('+QUOTENAME(Product)+',''0'')' +' AS '+QUOTENAME(Product) FROM #t FOR XML PATH ('')),1,1,'')
SELECT #SumCol=STUFF((SELECT DISTINCT ' + '+QUOTENAME(Product) FROM #t FOR XML PATH ('')),1,2,'')
SET #Sql='
SELECT *,('+#SumCol+') AS Total FROM
(
SELECT Payment,'+#ISNULLDyColumn+'
FROM
(
SELECT * FROM #t
)AS
SRC
PIVOT
(
SUM(AMOUNT) FOR Product IN ('+#DyColumn+')
) AS Pvt
)dt
'
PRINT #Sql
EXECUTE (#Sql)
Result
Payment A B C D Total
---------------------------------
Cash 19 0 0 42 61
Credit 0 39 26 0 65
I have a table as below
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB'
need output as below but without using any UDF.
Data String
------------
SQL S,Q,L
JOB J,O,B
Please help me on this
Sure you can :). You can make it shorter too...
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB';
With cte as
(
Select Data, Len(Data) DataLength, 1 level
From #t
Union All
Select Data, DataLength - 1, level + 1
From cte
Where DataLength > 1
),
cte2 as
(
Select Data, SUBSTRING(Data, DataLength, 1) DataLetter, level
From cte
),
cte3 as
(
Select Data,
(
SELECT DataLetter + ','
FROM cte2 c
Where c.Data = cte2.Data
Order By level desc
FOR XML PATH(''), TYPE
).value('.[1]', 'NVARCHAR(1000)') DataComa
From cte2
Group By Data
)
Select Data, substring(DataComa, 1, Len(DataComa) - 1) Data2
From cte3
Late to the party, but here's a slightly shorter version:
DECLARE #T TABLE(Data VARCHAR(MAX));
INSERT INTO #T VALUES('SQL'),('JOB'),('FLOOB');
;WITH n AS (SELECT TOP (SELECT MAX(LEN(Data)) FROM #T)
n = ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects
),
t AS (SELECT n, Data, Letter = SUBSTRING(t.Data, n.n, 1) FROM n
INNER JOIN #T AS t ON SUBSTRING(t.Data, n.n, 1) > ''
)
SELECT Data, STUFF((SELECT ',' + letter FROM t AS t2
WHERE t2.Data = t.Data ORDER BY t2.n FOR XML PATH(''),
TYPE).value(N'./text()[1]', N'varchar(max)'), 1, 1, '')
FROM t GROUP BY Data;
Results:
FLOOB F,L,O,O,B
JOB J,O,B
SQL S,Q,L
It is very easy to do with UDF.
But If you want with out UDF, the only one way I can think of is
something like this
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB'
select replace(replace(replace(data,'S','S,'),'Q','Q,'),'L','L,') from #T
here you have to replace all the 26 characters with replace function. ie, 'A' with 'A,' 'B' with 'B,' .... 'Z' with 'Z,'
Using the same approach I used for Initcap function here http://beyondrelational.com/modules/2/blogs/70/posts/10901/tsql-initcap-function-convert-a-string-to-proper-case.aspx
DECLARE #T TABLE(Data VARCHAR(MAX))
INSERT INTO #T
SELECT 'SQL' UNION ALL SELECT 'JOB'
select data,
upper(replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(
' '+data ,
' a','a,'),' b','b,'),'c','c,'),'d','d,'),'e','e,'),'f','f,'),
' g','g,'),' h','h,'),'i','i,'),'j','j,'),'k','k,'),'l','l,'),
' m','m,'),' n','n,'),'o','o,'),'p','p,'),'q','q,'),'r','r,'),
' s','s,'),' t','t,'),'u','u,'),'v','v,'),'w','w,'),'x','x,'),
' y','y,'),' z','z,')) as splitted_data
from
#t