In SQL Count number of distinct values corresponding to a same Id and DISPLAY in a same row - sql

I am stuck on a SQL query. I have results as below in a table and have to display the final result in a report as below:
id Question
-------------
13 ABC
13 ABC
13 QWE
13 ABC
13 QWE
13 ABC
Expected result:
id Result
--------------------
13 4 ABC, 2 QWE
Can somebody please help me out? Thank you.

This requires pre-aggregation and string aggregation.
with t as (
select id, question, count(*) as cnt
from t
group by id
)
select i.id,
stuff( (select ', ' + convert(varchar(255), cnt) + ' ' + question
from t t2
where t2.id = i.id
for xml path ('')
), 1, 2, ''
) as result
from (select distinct id from t) i;

--testdata-begin
if not object_id(N'Tempdb..#T') is null
drop table #T
Go
Create table #T([id] int,[Question] nvarchar(23))
Insert #T
select 13,N'ABC' union all
select 13,N'ABC' union all
select 13,N'QWE' union all
select 13,N'ABC' union all
select 13,N'QWE' union all
select 13,N'ABC'
Go
--testdata-end
WITH cte AS (
Select id,Question,COUNT(1) AS num from #T GROUP BY id,Question
)
SELECT id,
STUFF(
(
SELECT ',' + RTRIM(b.num) + ' ' + b.Question
FROM cte b
WHERE a.id = b.id
FOR XML PATH('')
),
1,
1,
''
) AS Result
FROM cte a
GROUP BY id;

Related

How to join rows in some table in SQL Server?

i want join some table then concatenation columns
MyTable
-------------------------------
ID RowId LangId Caption
-------------------------------
1 1 1 ڕۆشتن
2 1 2 Go
3 1 3 اذهب
4 2 1 ئاو
5 2 2 water
6 2 3 ماء
I want join concatenation Caption column ex: for RowId 1 'ڕۆشتن - Go - اذهب'
Desired output
--------------------
RowId Caption
--------------------
1 ڕۆشتن - Go - اذهب
2 ئاو- water - ماء
I seen link but can't help me
You can use string_agg():
select rowid,
string_agg(caption, ' ') within group (order by langid) as caption
from t
group by rowid;
You can use for xml for older vresion :
select r.rowid,
stuff( (select ' - '+t.caption
from table t
where t.rowid = r.rowid
order by t.LangId
for xml path('')
), 1, 1, ''
) as Caption
from (select distinct rowid from table ) r;
You can use string_agg() for newer version SQL Server 17+ :
select t.rowid,
string_agg(t.caption, ' ') within group (order by t.langid) as caption
from table t
group by t.rowid;
You can try the following query using for xml as shown below.
SELECT DISTINCT t.RowId,
STUFF((SELECT distinct ' - ' + t1.[Caption]
FROM
(
SELECT t.[RowId],
t2.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS t2 ON t2.[RowId] = t.[RowId]
)
t1
WHERE t.RowId = t1.RowId
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') CaptionList
FROM
(
SELECT t.[RowId],
por.Caption
FROM [TestTable] AS t
INNER JOIN [TestTable] AS por ON por.RowId = t.RowId
)t;
Here is the live db<>fiddle demo.
Try this:
SELECT DISTINCT RowID,
STUFF((SELECT DISTINCT Sub.caption +' - '
FROM #tab Sub
WHERE Sub.rowid = Main.rowid
FOR XML PATH('')), 1, 1, '') AS Caption
FROM #tab Main
I think this Query can perfectly fit for your case.

SQL Server: Merge two values with regexp

I am using an SQL Server database and have these following tables
Table "Data"
--------------------------------------------------------------------------------------------------|
| Id |col_1_type | col_1_name | col_2_type | col_2_name | col_3_type | col_3_name |
--------------------------------------------------------------------------------------------------|
| 1 |KI | Inflation Rate | KI | Currency Rate | MI | Government Spending |
--------------------------------------------------------------------------------------------------|
And i just want to make my result to be like this:
+----+------------------------+
| Id | results |
+----+------------------------+
| 1 | KI-Inflation Rate |
| 2 | KI-Currency Rate |
| 3 | MI-Government Spending |
+----+------------------------+
The column name is mandatory though, thats what made it complicated i guess?
i know you can merge 2 values or concatenate it, but i'm stuck on the column name such as col_1_name and col_2_type. Do i need to use regexp maybe?
Please try this-
select ROW_NUMBER() OVER (ORDER BY (select null)) id , results
from
(
SELECT CONCAT(col_1_type,'-',col_1_name) results
FROM [Data]
UNION ALL
SELECT CONCAT(col_2_type,'-',col_2_name)
FROM Data
UNION ALL
SELECT CONCAT(col_3_type,'-',col_3_name)
FROM Data
)o
Or this also
SELECT Id,results
FROM Data
CROSS apply
(VALUES (CONCAT(col_1_type,'-',col_1_name),1),(CONCAT(col_2_type,'-',col_2_name),2)
,(CONCAT(col_3_type,'-',col_3_name) ,3) ) cs (results,Id)
I would use of cross apply with the help of CTE and stuff()
;with cte as
(
select a.* from table t
cross apply (
values
(t.col_1_type, 'col_1'),
(t.col_1_name, 'col_1'),
(t.col_2_type, 'col_2'),
(t.col_2_name, 'col_2'),
(t.col_3_type, 'col_3'),
(t.col_3_name, 'col_3')
) a(name, id)
)
select distinct stuff(
(select '-'+name from cte where id= c.id for xml path('')),
1,1, ''
) [Results],
from cte c
EDIT :
Not sure about Id column but my guess that could be resolved by using ranking function
select row_number() over (order by (select 1)) Id,
cc.Results from
(
select distinct stuff(
(select '-'+id from cte where name = c.name for xml path('')),
1,1, ''
) [Results]
from cte c
) cc
Result :
Id Results
1 KI-Currency Rate
2 KI-Inflation Rate
3 MI-Government Spending
Sample data
IF OBJECT_ID('tempdb..#t') iS NOT NULL
DROP TABLE #t
IF OBJECT_ID('dbo.temp','U') iS NOT NULL
DROP TABLE temp
;With CTe(
Id ,col_1_type , col_1_name, col_2_type , col_2_name, col_3_type , col_3_name )
AS
(
SELECT 1,'KI','Inflation Rate','KI','Currency Rate','MI','Government Spending'
)
SELECT * INTO temp FROM CTe
Using Dynamic sql
DECLARE #Sqlstring nvarchar(max)
,#SQlQuery nvarchar(max)
;WITH cte
AS
(
SELECT COLUMN_NAME ,
((ROW_NUMBER()OVER(ORDER BY (SELECT NULL))-1)/2 )+1 AS BatchSeq
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='temp' AND COLUMN_NAME<>'Id'
)
SELECT #Sqlstring=STUFF((SELECT ', '+COLUMN_NAME FROM
(
SELECT DISTINCT '('+STUFF((SELECT ', '+COLUMN_NAME
FROM cte i
WHERE i.BatchSeq=o.BatchSeq FOR XML PATH ('')),1,1,'') +')' AS COLUMN_NAME
FROM cte o
)dt
FOR XML PATH ('')),1,1,'')
SET #SQlQuery='
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS ID,
CONCAT(col_type,''-'',Col_names) AS Result
FROM Temp
CROSS APPLY ( VALUES '+#Sqlstring+') dt(col_type,Col_names)'
PRINT #SQlQuery
EXEC(#SQlQuery)
Result
ID Result
-----------------------
1 KI-Inflation Rate
2 KI-Currency Rate
3 MI-Government Spending
Try like this
select (column1 || ' '|| column2) from table;
or
SELECT tablename.col_1_type + ' ' + tablename.col_1_name AS results;

SQL splitting a word in separate characters

I need to change an application and the first thing I need is to change a field in a database table.
In this table I now have 1 to 6 single characters, i.e. 'abcdef'
I need to change this to '[a][b][c][d][e][f]'
[edit] It is meant to stay in the same field. So before field = 'abcdef' and after field = '[a][b][c][d][e][f]'.
What would be a good way to do this?
rg.
Eric
You can split string to separate characters using following function:
create function ftStringCharacters
(
#str varchar(100)
)
returns table as
return
with v1(N) 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
),
v2(N) as (select 1 from v1 a, v1 b),
v3(N) as (select top (isnull(datalength(#str), 0)) row_number() over (order by ##spid) from v2)
select N, substring(#str, N, 1) as C
from v3
GO
And then apply it as:
update t
set t.FieldName = p.FieldModified
from TableName t
cross apply (
select (select quotename(s.C)
from ftStringCharacters(t.FieldName) s
order by s.N
for xml path(''), type).value('text()[1]', 'varchar(20)')
) p(FieldModified)
SQLFiddle sample
DECLARE #text NVARCHAR(50)
SET #text = 'abcdef'
DECLARE #texttable TABLE (value NVARCHAR(1))
WHILE (len(#text) > 0)
BEGIN
INSERT INTO #texttable
SELECT substring(#text, 1, 1)
SET #text = stuff(#text, 1, 1, '')
END
select * from #texttable
Without using a function:
declare #t table(C varchar(18))
insert #t values('abc'), ('1234'), (' 1234a')
;with CTE as
(
select C, '[' + substring(c, a.n, 1) + ']' v, rn from
(select 1 n union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6) a
cross apply
(select c, row_number() over (order by C) rn from #t group by c) b
where a.n <= len(C)
)
update t3
set C = t4.[value]
FROM #t t3
JOIN
(
select C,
(
select v
from CTE t1
where t1.rn = t2.rn
for xml path(''), type
).value('.', 'varchar(18)') [value]
from CTE t2
group by t2.rn, C
) t4
ON t3.C = t4.C
SELECT * FROM #t

JOIN three tables and aggregate data from multiple rows for every DISTINCT row in separate column

JOIN three tables and aggregate data from multiple rows for every DISTINCT row in separate column
i have a table where one item is mapped with multiple items.
Key 1 | Key 2
1 2
1 5
1 6
1 4
1 8
I have another table like this
Key 1 | ShortKey1Desc
1 'Desc short'
i have one more table where i have data like this
Key 1 | Description
1 'Desc a'
1 'Desc c'
1 'Desc aa'
1 'Desc tt'
i need to write a sql query for my view where table would be generated like this
Key 1 | AllKeys2ForKey1 | AllDescriptionsForKey1 | ShortKey1Desc
1 | 2;5;6;4;8 | Desc a; Desc c; Desc aa; Desc tt | Desc short
Key 1 is a string type field so i need to join them table using that string key
what i'm trying is to create a view for comfortable data access. need to create a query what will not take ages. i already tried to do it with Functions but it takes ages for load.
any help on this one would be highly appreciated. thanks a lot
Assuming that you are unable to change the data structures to make a more efficient query, this will work:
--Populate sample data
SELECT 1 as key1, 2 as key2 INTO #tbl1
UNION ALL SELECT 1, 5
UNION ALL SELECT 1, 6
UNION ALL SELECT 1, 4
UNION ALL SELECT 1, 8
SELECT 1 as key1, 'Desc short' as shortkeydesc INTO #tbl2
SELECT 1 as key1, 'Desc a' as [description] INTO #tbl3
UNION ALL SELECT 1, 'Desc c'
UNION ALL SELECT 1, 'Desc aa'
UNION ALL SELECT 1, 'Desc tt'
--Combine data into semi-colon separated lists
SELECT
key1
,STUFF(
(
SELECT
';' + CAST(t2.key2 AS VARCHAR(10))
FROM #tbl1 t2
WHERE t2.key1 = tbl1.key1
FOR XML PATH('')
), 1, 1, ''
)
,STUFF(
(
SELECT
';' + tbl2.shortkeydesc
FROM #tbl2 tbl2
WHERE tbl2.key1 = tbl1.key1
FOR XML PATH('')
), 1, 1, ''
)
,STUFF(
(
SELECT
';' + tbl3.[description]
FROM #tbl3 tbl3
WHERE tbl3.key1 = tbl1.key1
FOR XML PATH('')
), 1, 1, ''
)
FROM #tbl1 tbl1
GROUP BY tbl1.key1
to convert rows into one single result you will need to save values in a variable, below is sample code just to give you an idea
Declare #AllKeys2ForKey1 varchar(50)
set #AllKeys2ForKey1 = ''
SELECT #AllKeys2ForKey1 = #AllKeys2ForKey1 + cast([Key 2] as varchar(3)) + ','
FROM [AllKeys2ForKey1Table] where [KEY 1] = 1
Declare #AllDescriptionsForKey1 varchar(100)
set #AllDescriptionsForKey1 = ''
SELECT #AllKeys2ForKey1 = #AllKeys2ForKey1 + [Description] + ','
FROM [AllDescriptionsForKey1Table] where [KEY 1] = 1
Declare #ShortKey1Desc varchar(100)
set #ShortKey1Desc = ''
SELECT #ShortKey1Desc = #ShortKey1Desc + [ShortKey1Desc] + ','
FROM [ShortKey1DescTable] where [KEY 1] = 1
Select [KEY 1],
substring(#AllKeys2ForKey1,1,len(#AllKeys2ForKey1) - 1) as 'AllKeys2ForKey1 ',
substring(#AllDescriptionsForKey1,1,len(#AllDescriptionsForKey1) - 1) as 'AllDescriptionsForKey1',
substring(#ShortKey1Desc,1,len(#ShortKey1Desc) - 1) as 'ShortKey1Desc'
from Table where [KEY 1]= 1
You Must Write CLR Aggregate Function for Solving This Question.
for write CLR Aggregate Function :
1: Run Microsoft Visual Stadio
2: Create New Project
3: then Select Data Project
4: CLR Aggregate Function
After Create Your Aggregate Function Create Your Query Such as Below
Select A.Key1, OwnAggregateFn(B.Description), OwnAggregateFn(C.Key2), ...
From A
inner join B ON B.Key1 = A.Key1
inner join C ON C.Key1 = A.Key1
...
Group By A.Key1

SQL query problem

Let´s say I have two tables, "Garden" and "Flowers". There is a 1:n-relationship between these tables, because in a garden can be many flowers. Is it possible to write an SQL query which returns a result with the following structure:
GardenName Flower1Name Flower2Name .... (as long as there are entries in flowers)
myGarden rose tulip
CREATE TABLE #Garden (Id INT, Name VARCHAR(20))
INSERT INTO #Garden
SELECT 1, 'myGarden' UNION ALL
SELECT 2, 'yourGarden'
CREATE TABLE #Flowers (GardenId INT, Flower VARCHAR(20))
INSERT INTO #Flowers
SELECT 1, 'rose' UNION ALL
SELECT 1, 'tulip' UNION ALL
SELECT 2, 'thistle'
DECLARE #ColList nvarchar(max)
SELECT #ColList = ISNULL(#ColList + ',','') + QUOTENAME('Flower' + CAST(ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS VARCHAR))
FROM #Flowers WHERE GardenId = (
SELECT TOP 1 GardenId
FROM #Flowers
ORDER BY COUNT(*) OVER (PARTITION BY GardenId) DESC
)
EXEC (N'
;WITH cte As
(
SELECT *, ''Flower'' + CAST(ROW_NUMBER() OVER (PARTITION BY GardenId ORDER BY (SELECT 0)) AS VARCHAR) RN
FROM #Flowers F
)
SELECT Name,' + #ColList + N'
FROM cte
JOIN #Garden g ON g.Id = GardenId
PIVOT (MAX(Flower) FOR RN IN (' + #ColList + N')) Pvt')
DROP TABLE #Garden
DROP TABLE #Flowers
Returns
Name Flower1 Flower2
-------------------- -------------------- --------------------
myGarden rose tulip
yourGarden thistle NULL
Look at using Pivot in SQL Server. Here is a good link that goes over how it works:
http://www.kodyaz.com/articles/t-sql-pivot-tables-in-sql-server-tutorial-with-examples.aspx
Ok, i think i got it working. Try this:
with temp as
(
select 'myGarden' as name, 'test1' as flower
union
select 'myGarden','test2'
union
select 'myGarden','test5'
union
select 'abeGarden','test4'
union
select 'abeGarden','test5'
union
select 'martinGarden', 'test2'
)
select* from temp
pivot
(
max(flower)
for flower in (test1,test2,test3,test4,test5)
) PivotTable
You could also make the values in the in clause dynamic. Since this is a CTE i can't in my example.
Dynamic SQL with a cursor is the only way I can think of, and it won't be pretty.
If you only want the results for one garden at a time this would give you the data:
select gardenName from tblGarden where gardenid = 1
Union ALL
select tblFLowers.flowerName from tblFlowers where gardenid = 1