SQL - String concatenated grouping WITHOUT DISTINCT - sql

I'm wondering if this is possible - I have a table like this:
pk int, num int, name varchar(1)
1 1 'a'
2 1 'b'
3 1 'c'
4 1 'd'
5 1 'e'
6 2 'f'
7 2 'g'
8 2 'h'
9 2 'i'
10 2 'j'
And I'd like an output like this WITHOUT using a DISTINCT clause:
num result
1 a,b,c,d,e
2 f,g,h,i,j
Here are ddl statements for testing:
declare #tbl table (pk int, num int, name varchar(1))
insert into #tbl select 1, 1, 'a'
insert into #tbl select 2, 1, 'b'
insert into #tbl select 3, 1, 'c'
insert into #tbl select 4, 1, 'd'
insert into #tbl select 5, 1, 'e'
insert into #tbl select 6, 2, 'f'
insert into #tbl select 7, 2, 'g'
insert into #tbl select 8, 2, 'h'
insert into #tbl select 9, 2, 'i'
insert into #tbl select 10, 2, 'j'
The following query works, but I'd like to eliminate the DISTINCT clause if possible:
select DISTINCT num, stuff((select ',' + name from #tbl where num = t.num for xml path('')), 1, 1, '')
from #tbl t
Any idea how to do this in SQL 2012+?

If you don't have a list of num values that you want, then you can create one. One rather silly way is:
select t.num,
stuff( (select ',' + name
from #tbl t2
where t2.num = t.num
for xml path('')
), 1, 1, '')
from (values (1), (2)) as t(num);
More commonly, this would be written as:
select t.num,
stuff( (select ',' + name
from #tbl t2
where t2.num = t.num
for xml path('')
), 1, 1, '')
from (select distinct num from #tbl) t;

Try this I think it will work fine
select num, group_concat(name) from table_name group by num;

Related

Rows to single cell

I would like to get the desired output marked in green
the data points for each id get put into a single cell
Basically take all the events that have happened with A and attach it in the same order
Use Stuff Function:
DECLARE #tblTest AS Table(
ID INT,
EVENT VARCHAR(5)
)
INSERT INTO #tblTest VALUES
(1,'A'),
(1,'A'),
(1,'C'),
(2,'A'),
(2,'B'),
(2,'C')
SELECT DISTINCT
T1.ID,
STUFF
(
(SELECT '' + convert(varchar(10), T2.EVENT, 120)
FROM #tblTest T2
where T1.ID = T2.ID
FOR XML PATH (''))
, 1, 0, '') AS EVENT
FROM #tblTest T1
You can use FOR XML:
SELECT DISTINCT
ID,
(SELECT [EVENT] +''
FROM YourTable
WHERE ID = y.ID
FOR XML PATH('')
) as [EVENT]
FROM YourTable y
Output:
ID EVENT
1 AABCD
2 AABBCC
You can use UDF to do so as follows:
CREATE TABLE t(
id INT,
col CHAR(1)
);
INSERT INTO t VALUES (1,'a');
INSERT INTO t VALUES (1,'b');
INSERT INTO t VALUES (1,'c');
INSERT INTO t VALUES (1,'d');
INSERT INTO t VALUES (2,'e');
INSERT INTO t VALUES (2,'f');
INSERT INTO t VALUES (3,'g');
INSERT INTO t VALUES (4,'h');
The UDF (User defined function) -
USE [t]
GO
CREATE FUNCTION dbo.ConcatenateCols(#Id INT)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #RtnStr VARCHAR(MAX)
SELECT #RtnStr = COALESCE(#RtnStr + '','') + col
FROM dbo.t
WHERE id = #Id AND col > ''
RETURN #RtnStr
END
GO
Finally the query and result:
SELECT id, dbo.ConcatenateCols(id) AS Cols -- UDF - ConcatenateCols(id)
FROM t GROUP BY Id
CREATE TABLE #temp(Id INt,Event Nvarchar(25))
INSERT INTO #temp
SELECT 1,
'A'
UNION ALL
SELECT 1,
'A'
UNION ALL
SELECT 1,
'B'
UNION ALL
SELECT 1,
'C'
UNION ALL
SELECT 1,
'D'
UNION ALL
SELECT 2,
'A'
UNION ALL
SELECT 2,
'A'
UNION ALL
SELECT 2,
'B'
UNION ALL
SELECT 2,
'B'
UNION ALL
SELECT 2,
'C'
UNION ALL
SELECT 2,
'C'
SELECT DISTINCT ID,
(SELECT [EVENT] +''
FROM #temp
WHERE ID = y.ID
FOR XML PATH('') ) AS [EVENT]
FROM #temp y

Select statement from sql table

Here is some QuestionID and OptionID with IsCorrect flag for every option, I am not getting a way to get the result set in the required format(shown in attached Image), it will be helpful if some one give some sort of suggestion with best way to achieve this, Thanks in Advance,
declare #temp table
(questionID int, optionID int, isCorrect bit, option_text varchar(50))
insert into #temp values
( 1, 1, 1, 'abc'), (1, 2, 1, 'pqr'), (1, 3, 0, 'mno' ),
(2, 1, 1, 'xyz' ), (2, 2, 1, 'def')
Try this:
SELECT T1.questionId
,option_text = SubString (( SELECT ', ' + T2.option_text
FROM #temp as T2
WHERE T1.questionId = T2.questionId
and isCorrect = 1
FOR XML PATH ( '' ) ), 3, 1000)
, 'key'as Answer
FROM #temp as T1
GROUP BY questionId
union all
select Questionid, Option_text, 'primary_key'
from #temp
order by 1
Create A Comma Delimited List From a Column
I think it's using GROUP_CONCAT function in SQL Server (2012?):
My answer is:
SQL Fiddle
SELECT
questionID AS [QuestionID]
, option_text AS [OptionText]
, CASE WHEN (CHARINDEX(',', option_text) > 0) THEN 'key'
WHEN (isCorrect = 1) THEN 'partial key'
ELSE 'wrong key' END AS [Answere]
FROM
(
SELECT questionID, option_text, isCorrect
FROM #temp
UNION ALL
SELECT
questionID
, LEFT(keys, LEN(keys) - 1)
, isCorrect
FROM
(
SELECT questionID
, (SELECT option_text + ','
FROM #temp
WHERE questionID = t.questionID AND isCorrect = 1
FOR XML PATH('')
) AS keys
, isCorrect
FROM #temp AS t
GROUP BY questionID, isCorrect
HAVING isCorrect = 1
) AS connected
) AS result
ORDER BY QuestionID, Answere

Select only distinct values from two columns from a table

If I have a table such as
1 A
1 B
1 A
1 B
2 C
2 C
And I want to select distinct from the two columns so that I would get
1
2
A
B
C
How can I word my query? Is the only way to concatenate the columns and wrap them around a distinct function operator?
You could use a union to create a table of all values from both columns:
select col1 as BothColumns
from YourTable
union
select col2
from YourTable
Unlike union all, union removes duplicates, even if they come from the same side of the union.
SQL Fiddle
Why even distinct in Union, try this :
select cast(id as char(1)) from test
union
select val from test
Please try:
Select Col1 from YourTable
union
Select Col2 from YourTable
UNION removes duplicate records (where all columns in the results are the same), UNION ALL does not.
Please check What is the difference between UNION and UNION ALL
For multiple columns, you can go for UNPIVOT.
SELECT distinct DistValues
FROM
(SELECT Col1, Col2, Col3
FROM YourTable) p
UNPIVOT
(DistValues FOR Dist IN
(Col1, Col2, Col3)
)AS unpvt;
Try this one -
DECLARE #temp TABLE
(
Col1 INT
, Col2 NVARCHAR(50)
)
INSERT INTO #temp (Col1, Col2)
VALUES (1, 'ab5defg'), (2, 'ae4eii')
SELECT disword = (
SELECT DISTINCT dt.ch
FROM (
SELECT ch = SUBSTRING(t.mtxt, n.number + 1, 1)
FROM [master].dbo.spt_values n
CROSS JOIN (
SELECT mtxt = (
SELECT CAST(Col1 AS VARCHAR(10)) + Col2
FROM #temp
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'
)
) t
WHERE [type] = N'p'
AND number <= LEN(mtxt) - 1
) dt
FOR XML PATH(''), TYPE).value('.', 'VARCHAR(MAX)'
)
Or try this -
DECLARE #temp TABLE
(
a CHAR(1), b CHAR(1)
)
INSERT INTO #temp (a, b)
VALUES
('1', 'A'), ('1', 'B'), ('1', 'A'),
('1', 'B'), ('2', 'C'), ('2', 'C')
SELECT a
FROM #temp
UNION
SELECT b
FROM #temp
Because what you want select is in different columns, you can use union like below:
select distinct tarCol from
(select distinct column1 as tarCol from table
union
select distinct column2 from table) as tarTab
You can use like this to get multiple distinct column values
(SELECT DISTINCT `enodeb` as res,
"enodeb" as columnname
FROM `raw_metrics`)
UNION
(SELECT DISTINCT `interval` as res,
"interval" as columnname
FROM `raw_metrics`)

SQL statement to select the value of the latest version of data based on the latest date

I have a simple query and am wondering if it could be more elegantly coded. The final solution has to be ansi-compliant.
I need to fetch the latest value from a table based on date and version. A sample would explain more clearly:
declare #t table (id int, due_date smalldatetime, version int, value nvarchar(10))
insert into #t select 3, '1/1/2010', 1, 'value 1'
insert into #t select 3, '1/1/2010', 2, 'value 2'
insert into #t select 3, '3/1/2010', 1, 'value 3'
insert into #t select 3, '3/1/2010', 2, 'value 4'
insert into #t select 3, '3/1/2010', 3, 'value 5'
insert into #t select 3, '3/1/2010', 4, 'value 6'
insert into #t select 3, '4/1/2010', 1, 'value 7'
insert into #t select 3, '4/1/2010', 2, 'value 8'
insert into #t select 3, '4/1/2010', 3, 'value 9'
select value from #t t
inner join (select due_date, version=max(version)
from #t where due_date = (select max(due_date) from #t) group by due_date) maxes
on t.due_date=maxes.due_date and t.version=maxes.version
So I would expect the output to be
value 9
which it is based on the above query.
I'm not particulary happy with this solution - any better ways to accomplish this?
You could use:
SELECT TOP 1
x.value
FROM #t x
ORDER BY x.due_date DESC, x.version DESC
TOP is not ANSI, though. Another option would be to use ANSI analytical/rank/windowing functions:
SELECT x.value
FROM (SELECT t.value,
ROW_NUMBER() OVER (ORDER BY t.due_date DESC, t.version DESC) AS rank
FROM #t t) x
WHERE x.rank = 1
But this requires a database that supports the functionality - MySQL doesn't, PostgreSQL only started in v8.4...
SELECT
value
FROM
#t T1
LEFT OUTER JOIN #t T2 ON
T2.id = T1.id AND
(
(T2.due_date > T1.due_date) OR
(T2.due_date = T1.due_date AND T2.version > T1.version)
)
WHERE
T2.id IS NULL
or...
SELECT
value
FROM
#t T1
WHERE
NOT EXISTS
(
SELECT
FROM
#t T2
WHERE
T2.id = T1.id AND
(
(T2.due_date > T1.due_date) OR
(T2.due_date = T1.due_date AND T2.version > T1.version)
)
)

t-sql recursive query

Based on an existing table I used CTE recursive query to come up with following data. But failing to apply it a level further.
Data is as below
id name parentid
--------------------------
1 project 0
2 structure 1
3 path_1 2
4 path_2 2
5 path_3 2
6 path_4 3
7 path_5 4
8 path_6 5
I want to recursively form full paths from the above data. Means the recursion will give the following output.
FullPaths
-------------
Project
Project\Structure
Project\Structure\Path_1
Project\Structure\Path_2
Project\Structure\Path_3
Project\Structure\Path_1\path_4
Project\Structure\Path_2\path_5
Project\Structure\Path_3\path_6
Thanks
Here's an example CTE to do that:
declare #t table (id int, name varchar(max), parentid int)
insert into #t select 1, 'project' , 0
union all select 2, 'structure' , 1
union all select 3, 'path_1' , 2
union all select 4, 'path_2' , 2
union all select 5, 'path_3' , 2
union all select 6, 'path_4' , 3
union all select 7, 'path_5' , 4
union all select 8, 'path_6' , 5
; with CteAlias as (
select id, name, parentid
from #t t
where t.parentid = 0
union all
select t.id, parent.name + '\' + t.name, t.parentid
from #t t
inner join CteAlias parent on t.parentid = parent.id
)
select *
from CteAlias
Try something like this:
WITH Recursive AS
(
SELECT
ID,
CAST(PathName AS VARCHAR(500)) AS 'FullPaths',
1 AS 'Level'
FROM
dbo.YourTable
WHERE
ParentID = 0
UNION ALL
SELECT
tbl.ID,
CAST(r.FullPaths + '\' + tbl.PathName AS VARCHAR(500)) AS 'FullPaths',
r.Level + 1 AS 'Level'
FROM
dbo.YourTable tbl
INNER JOIN
Recursive r ON tbl.ParentID = r.ID
)
SELECT * FROM Recursive
ORDER BY Level, ID
Output:
ID FullPaths Level
1 project 1
2 project\structure 2
3 project\structure\path_1 3
4 project\structure\path_2 3
5 project\structure\path_3 3
6 project\structure\path_1\path_4 4
7 project\structure\path_2\path_5 4
8 project\structure\path_3\path_6 4
try this:
DECLARE #YourTable table (id int, nameof varchar(25), parentid int)
INSERT #YourTable VALUES (1,'project',0)
INSERT #YourTable VALUES (2,'structure',1)
INSERT #YourTable VALUES (3,'path_1',2)
INSERT #YourTable VALUES (4,'path_2',2)
INSERT #YourTable VALUES (5,'path_3',2)
INSERT #YourTable VALUES (6,'path_4',3)
INSERT #YourTable VALUES (7,'path_5',4)
INSERT #YourTable VALUES (8,'path_6',5)
;WITH Rec AS
(
SELECT
CONVERT(varchar(max),nameof) as nameof,id
FROM #YourTable
WHERE parentid=0
UNION ALL
SELECT
CONVERT(varchar(max),r.nameof+'\'+y.nameof), y.id
FROM #yourTable y
INNER jOIN Rec r ON y.parentid=r.id
)
select * from rec
output:
nameof
-----------------------------------------------
project
project\structure
project\structure\path_1
project\structure\path_2
project\structure\path_3
project\structure\path_3\path_6
project\structure\path_2\path_5
project\structure\path_1\path_4
(8 row(s) affected)
Something like
;WITH MyCTE AS
(
SELECT
name AS FullPaths, id
FROM
MyTable
WHERE
parentid = 0 /*Normally it'd be IS NULL with an FK linking the 2 columns*/
UNION ALL
SELECT
C.FullPaths + '\' + M.name, M.id
FROM
MyCTE C
JOIN
MyTable M ON M.parentid = C.id
)
SELECT FullPaths FROM MyCTE
You'll have to change the name of #test table I was using.
WITH cte(id, name, parentid) AS
(
SELECT id, convert(varchar(128), name), parentid
FROM #test
WHERE parentid = 0
UNION ALL
SELECT t.id, convert(varchar(128), c.name +'\'+t.name), t.parentid
FROM #test t
INNER JOIN cte c
ON c.id = t.parentid
)
SELECT name as FullPaths
FROM cte
order by id