Rows to single cell - sql

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

Related

Select data from one table base on selection from another table in SQL

I have these 3 table
First contain the item with price on given dates
2nd is the table of items
3rd is the table of dates in which we want to show the price of 2nd table item on every date
if duration is not available on first table it should be 0
with myTable ( item,startdate,enddate,price) as
(
select 'AAAA' ,'16-3-2020','19-3-2020','50' union all
select 'AAAA' ,'16-4-2020','19-4-2020','70' union all
select 'BBB' ,'16-3-2020','19-3-2020','20' union all
select 'BBB' ,'16-4-2020','19-4-2020','90' union all
select 'CCC' ,'16-3-2020','29-3-2020','45' union all
select 'CCC' ,'16-4-2020','19-4-2020','120'
)
select item,startdate,enddate,price from myTable
GO
with itemTable ( item) as
(
select 'AAAA' union all
select 'BBB' union all
select 'CCC'
)
select item from itemTable
GO
with DateTable ( dateItem) as
(
select '16-3-2020' union all
select '19-4-2020' union all
select '20-3-2020'
)
select dateItem from DateTable
GO
and my desire result should be like this (above is dynamic data)
with mydesireTable (item, [16-3-2020],[19-4-2020],[20-3-2020]) as
(
select 'AAAA' ,'50','70','0' union all ---0 as its not on above data in duration
select 'BBB' ,'20','90','0' union all
select 'CCC' ,'45','120','45'
)
select item, [16-3-2020],[19-4-2020],[20-3-2020] from mydesireTable
I am not sure what to search for :) as i want to write query for it which return my desire table as data ( or as in temporary table )
One of many ways to do this. This is a static crosstab. You need to list out all the columns explicitly (twice)
If your columns are dynamic, you need to use a dynamic crosstab. You should also consider doing this in your "presentation" layer, i.e. excel or whatever you are handing this over in.
You should consider what you want when something in mytable appears against a bucket twice (this solution will add the prices)
with myTable ( item,startdate,enddate,price) as
(
select 'AAAA' ,CAST('2020-03-16' AS DATE),CAST('2020-03-19' AS DATE),50 union all
select 'AAAA' ,'2020-04-16','2020-04-19',70 union all
select 'BBB' ,'2020-03-16','2020-03-19',20 union all
select 'BBB' ,'2020-04-16','2020-04-19',90 union all
select 'CCC' ,'2020-03-16','2020-03-29',45 union all
select 'CCC' ,'2020-04-16','2020-04-19',120
),
itemTable ( item) as
(
select 'AAAA' union all
select 'BBB' union all
select 'CCC'
)
,DateTable ( dateItem) as
(
select CAST('2020-03-16' AS DATE) union all
select '2020-04-19' union all
select '2020-03-20'
)
SELECT item,
[2020-03-16],[2020-04-19], [2020-03-20]
FROM
(
select item, dateitem, price from myTable
inner join datetable on datetable.dateItem between mytable.startdate and myTable.enddate
) As Src
PIVOT
(
SUM(price)
FOR
dateitem IN ([2020-03-16],[2020-03-20],[2020-04-19])
) as P
IF OBJECT_ID('tempdb..#myTable', 'U') IS NOT NULL
DROP TABLE #myTable;
IF OBJECT_ID('tempdb..#itemTable', 'U') IS NOT NULL
DROP TABLE #itemTable;
IF OBJECT_ID('tempdb..#DateTable', 'U') IS NOT NULL
DROP TABLE #DateTable;
CREATE TABLE #myTable (
item VARCHAR(MAX) NOT NULL,
startdate DATE NOT NULL,
enddate DATE NOT NULL,
price INT NOT NULL DEFAULT(0)
);
INSERT #myTable (item, startdate, enddate, price) VALUES
('AAAA' ,CAST('2020-03-16' AS DATE),CAST('2020-03-19' AS DATE),50),
('AAAA' ,'2020-04-16','2020-04-19',70),
('BBB' ,'2020-03-16','2020-03-19',20),
('BBB' ,'2020-04-16','2020-04-19',90),
('CCC' ,'2020-03-16','2020-03-29',45),
('CCC' ,'2020-04-16','2020-04-19',120)
CREATE TABLE #itemTable (
item VARCHAR(MAX) NOT NULL
)
INSERT #itemTable (item) VALUES
('AAAA'),
('BBB'),
('CCC')
CREATE TABLE #DateTable (
dateItem DATE NOT NULL
)
INSERT #DateTable (dateItem) VALUES
(CAST('2020-03-16' AS DATE)),
(CAST('2020-04-19' AS DATE)),
(CAST('2020-03-20' AS DATE)),
(CAST('2020-03-21' AS DATE)),
(CAST('2021-03-21' AS DATE)),
(CAST('2022-03-21' AS DATE))
Declare #DynamicCol nvarchar(max),#DynamicColNull nvarchar(max)
,#Sql nvarchar(max)
SELECT #DynamicColNull=STUFF((SELECT DISTINCT ', '+'ISNULL('+QUOTENAME(dateItem),','+'''0'''+') As '+QUOTENAME(dateItem)
FROM #DateTable FOR XML PATH ('')),1,2,'')
SELECT #DynamicCol=STUFF((SELECT DISTINCT ', '+QUOTENAME(dateItem) FROM #DateTable FOR XML PATH ('')),1,2,'')
SET #Sql='SELECT [item], '+#DynamicColNull+' From
(
select item, dateitem, price from #myTable
inner join #datetable on #datetable.dateItem between #mytable.startdate and #myTable.enddate
)
AS Src
PIVOT
(
SUM(price) FOR [dateitem] IN ('+#DynamicCol+')
)AS Pvt'
PRINT #Sql
EXEC(#Sql)

SQL - String concatenated grouping WITHOUT DISTINCT

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;

SQL inserted value of a table as column of another table

Table1 Table2
Id Name Id Table1Id Value
1 Some 1 1 value1
2 Some1 2 2 value2
3 Some2 3 3 value3
4 Some3
.
.
I want to result this:
Some Some1 Some2 Some3
value1 value2 value3 NULL
When I entered value into the Table1 I want to look like the table2's column, how can I do this?
I guess I'm looking for pivot query.
Thanks for posting the additional information. Based on what you said, you're looking for a PIVOT statement that will turn each name in Table1 into a column header, containing the corresponding value from Table2.
Have a look at the following SQL, which produces the output your sample data indicates.
CREATE TABLE #Table1 (ID INT, Name varchar(5))
CREATE TABLE #Table2 (ID INT, Table1ID INT, Value varchar(6))
INSERT INTO #Table1 (ID, Name) SELECT 1, 'Some'
INSERT INTO #Table1 (ID, Name) SELECT 2, 'Some1'
INSERT INTO #Table1 (ID, Name) SELECT 3, 'Some2'
INSERT INTO #Table1 (ID, Name) SELECT 4, 'Some3'
INSERT INTO #Table2 (ID, Table1ID, Value) SELECT 1, 1, 'Value1'
INSERT INTO #Table2 (ID, Table1ID, Value) SELECT 2, 2, 'Value2'
INSERT INTO #Table2 (ID, Table1ID, Value) SELECT 3, 3, 'Value3'
-- List of values to be columns
declare #cols nvarchar(max)
select #cols = coalesce(#cols+N',', N'') + quotename(Name) from #Table1 order by ID
PRINT #Cols
declare #query varchar(MAX)
SET #query = '
SELECT *
FROM
(
SELECT T1.Name, T2.Value
FROM
#Table1 T1
INNER JOIN
#Table2 T2 ON
T1.ID = T2.Table1ID
) s
PIVOT
(
MAX(Value) FOR Name IN ('+#Cols+')
) p'
PRINT #query
EXEC (#query)
DROP TABLE #Table1
DROP TABLE #Table2

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)
)
)