how to convert varchar columns to comma separated row values - sql

id Name
1 A
2 C
2 D
1 B
Output required through T-sql
id Name
1 A,B
2 C,D

Use this
;
WITH SampleData
AS ( SELECT *
FROM ( VALUES ( 1, 'A'), ( 2, 'C'), ( 2, 'D'), ( 1, 'B'),
( 1, 'C' ) ) T ( id, name )
)
SELECT DISTINCT
B.Id ,
SUBSTRING(( SELECT ',' + Name
FROM SampleData AS A
WHERE A.Id = B.ID
FOR
XML PATH('')
), 2, 1000) AS NAME
FROM SampleData AS B
output result

Here's another option:
SELECT test.id, test.Name+','+ Table1_1.Name AS Name
FROM (SELECT MIN(Name) AS Name, id
FROM Table1
GROUP BY id) AS test RIGHT OUTER JOIN
Table1 AS Table1_1 ON test.id = Table1_1.id
where test.Name <>Table1_1.Name

Related

How to compare column in one table with array from another table in BigQuery?

Just continue from the answer for my previous question.
I want to get all values from table b (in rows) if there is any difference between values in arrays from table a by same ids
WITH a as (SELECT 1 as id, ['123', 'abc', '456', 'qaz', 'uqw'] as value
UNION ALL SELECT 2, ['123', 'wer', 'thg', '10', '200']
UNION ALL SELECT 3, ['200']
UNION ALL SELECT 4, null
UNION ALL SELECT 5, ['140']),
b as (SELECT 1 as id, '123' as value
UNION ALL SELECT 1, 'abc'
UNION ALL SELECT 1, '456'
UNION ALL SELECT 1, 'qaz'
UNION ALL SELECT 1, 'uqw'
UNION ALL SELECT 2, '123'
UNION ALL SELECT 2, 'wer'
UNION ALL SELECT 2, '10'
UNION ALL SELECT 3, null
UNION ALL SELECT 4, 'wer'
UNION ALL SELECT 4, '234'
UNION ALL SELECT 5, '140'
UNION ALL SELECT 5, '121'
)
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF(b.value IS NULL) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
It works well for all ids except 5.
In my case I need to return all values if there is any difference.
In example array with id 5 from table a has only one value is '140' while there are two rows with values by id 5 from table b. So in this case all values by id 5 from table b also must appear in expected output
How need to modify this query to get what I want?
UPDATED
Seems like it works for me. But I can not be sure for 100%
SELECT * EXCEPT(flag)
FROM (
SELECT b.*, COUNTIF((b.value IS NULL AND a.value IS NOT NULL) OR (b.value IS NOT NULL AND a.value IS NULL)) OVER(PARTITION BY id) flag
FROM a LEFT JOIN a.value
FULL OUTER JOIN b
USING(id, value)
)
WHERE flag > 0
AND NOT id IS NULL
#standardSQL
SELECT *
FROM table_b
WHERE id IN (
SELECT id FROM table_a a
JOIN table_b b USING(id)
GROUP BY id
HAVING STRING_AGG(IFNULL(b.value, 'NULL') ORDER BY b.value) !=
IFNULL(ANY_VALUE((SELECT STRING_AGG(IFNULL(value, 'NULL') ORDER BY value) FROM a.value)), 'NULL')
)

SQL Dynamic Self Cross Join using TSql

I have a table that has data like this
Group Value
1 A
1 B
1 C
2 F
2 G
3 J
3 K
I want to join all members of one group to all members of each of the other groups into a single column like this:
AFJ
AFK
AGJ
AGK
BFJ
BFK
BGJ
BGK
CFJ
CFK
CGJ
CGK
There can be n number of groups and n number of values
Thank you
SQL does not offer many options for such a query. The one standard method is a recursive CTE. Other methods would involve recursive functions or stored procedures.
The following is an example of a recursive CTE that solves this problem:
with groups as (
select v.*
from (values (1, 'a'), (1, 'b'), (1, 'c'), (2, 'f'), (2, 'g'), (3, 'h'), (3, 'k')
) v(g, val)
),
cte as (
select 1 as groupid, val, 1 as lev
from groups
where g = 1
union all
select cte.groupid + 1, cte.val + g.val, lev + 1
from cte join
groups g
on g.g = cte.groupid + 1
)
select val
from (select cte.*, max(lev) over () as max_lev
from cte
) cte
where lev = max_lev
order by 1;
Some databases that support recursive CTEs don't use the recursive keyword.
Here is a db<>fiddle.
If you need to consider gaps between the groups, i.e. Group 1, then no 2, then 3.
with t(Grp, val) as (
select 1, 'A' from dual
union all
select 1, 'B' from dual
union all
select 1, 'C' from dual
union all
select 2, 'F' from dual
union all
select 2, 'G' from dual
union all
select 3, 'J' from dual
union all
select 3, 'K' from dual
)
, grpIndexes as (
SELECT ROWNUM indx
, grp
FROM (select distinct
grp
from t)
)
, Grps as (
select t.*
, grpIndexes.indx
from t
inner join grpIndexes
on grpIndexes.grp = t.grp
)
, rt(val, indx, lvl) as (
select val
, indx
, 0 as lvl
from Grps
where indx = (select min(indx) from Grps)
union all
select previous.val || this.val as val
, this.indx
, previous.lvl + 1 as lvl
from rt previous
, Grps this
where this.indx = (select min(indx) from Grps where indx > previous.indx)
and previous.indx <> (select max(indx) from Grps)
)
select val
from rt
where lvl = (select max(lvl) from rt)
order by val
;
Note that I renamed the columns because Group and Value are reserved words.

Looking for a solution to retrieve either 1 or max of the value from a table in SQL

Table A
Owner row_no category
A 1 U
B 1 T
B 2 T
C 1 U
C 2 T
C 3 U
C 4 U
I'm looking for a solution that stores values into other table which should retrieve
row_no as 1 if the value is 1 and should return max(row_no)-1 if
the value isn't 1.
category should be either T or U or both based on whether an owner
has opted for only T or U or both in TABLE A.
Resultant table should be something like below.
Table B
Owner row_no category
A 1 U
B 1 T
C 3 Both
I tried using the below approach which turns out to be an error.
SELECT * INTO B FROM A
WHERE
ROW_NO LIKE CASE
WHEN ROW_NO=1 then ROW_NO
ELSE max(ROW_NO)-1
END
Haven't figured out yet on retrieving the category!
Could you please help with right approach ?!
NEW EDIT
I think you can do like this:
declare #table table (owner nvarchar(50),row_no int)
insert into #table
values
('A', 1),
('B', 1),
('B', 2),
('C', 1),
('C', 2),
('C', 3),
('C', 4)
select owner,row_no from (
select *, ROW_NUMBER() over(partition by owner order by la desc) as rn from (
select *,LEAD(row_no,1,1) over(partition by owner order by row_no) as la from #table
)X
)z where rn = 1
EDIT UPDATE
With categoryList you can do like this
declare #table table (owner nvarchar(50),row_no int,category nvarchar(50))
insert into #table
values
('A', 1,'U'),
('B', 1,'T'),
('B', 2,'T'),
('C', 1,'U'),
('C', 2,'T'),
('C', 3,'U'),
('C', 4,'U')
;
with category as (
select owner, categoryList = stuff((select N', ' + Category
from (Select distinct owner,category from #table t2
) z
where z.owner = t1.owner
FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 2, N'')
from #table t1
group by owner
)
select z.owner,row_no,y.categoryList from (
select *, ROW_NUMBER() over(partition by owner order by la desc) as rn from (
select *,LEAD(row_no,1,1) over(partition by owner order by row_no) as la from #table
)X
)z
inner join category y on z.owner = y.owner
where rn = 1
DBFiddeldemo
You could use:
WITH cte AS(
SELECT *, MAX(row_no)OVER(PARTITION BY owner) AS m
FROM tab
)
SELECT owner, row_no
INTO tab2
FROM cte
WHERE row_no = m-1 OR m=1;
DBFiddle Demo
Warning! I've made an assumption that values in row_no are consecutive.
Without cte/subquery:
SELECT TOP(1) WITH TIES *
INTO tabB
FROM tab
ORDER BY IIF(MAX(row_no)OVER(PARTITION BY owner) IN (row_no+1,1),0,1)
DBFiddle Demo2

select where a and next is c, skipping b

I am wondering if it is possible in SQL to return a single row to show, using the table below as an example, only a row for id 2:
table1 ( id 2 and 4 are missing value b)
id value
1 a
1 b
1 c
1 d
2 a
2 c
2 d
3 a
3 b
3 c
3 d
4 a
4 c
4 d
i basically want to find all instances where 'b' does not exist but 'a' still does exist for any id and return a single row for that any given id. i have tried something like this, but its not working as i would want it to:
select * from table1
where not exists (select distinct value from table1 where value b)
i would like the end result to be something this, identifying the values where 'b' does not exist but 'a' does(not showing the value, is unneeded for final goal):
result table
id
2
4
SELECT id
FROM table1 t1
WHERE
value = 'a'
AND NOT EXISTS (
SELECT *
FROM table1 sub
WHERE sub.id = t1.id AND sub.value = 'b'
)
This should do the job:
select distinct id
from table1 t
where not exists (
select 1
from table1 tt
where t.id = tt.id and tt.vallue = 'b'
)
and exists (
select 1
from table1 tt
where t.id = tt.id and tt.vallue = 'a'
)
Below you have shorter form. It may perform better and distinct keyword may be unnecessary if the pair (id, value) is unique.
select distinct id
from table1 t
left join table1 tt
on t.id = tt.id and tt.value = 'b'
where t.value = 'a'
and tt.id is null
Haven't tested, but I think something like this would work.
SELECT id FROM table1
WHERE value='a' AND id NOT IN(SELECT id FROM table1 WHERE value='b')
GROUP BY id;
EDIT: Apologies to Dooh. I just noticed that this answer is essentially a duplicate of Dooh's second query. I'll leave it as a runnable example.
It may be enlightening to compare execution plans for the various queries.
declare #table1 as table ( id int, value varchar(10) )
insert into #table1 ( id, value ) values
( 1, 'a' ), ( 1, 'b' ), ( 1, 'c' ), ( 1, 'd' ),
( 2, 'a' ), ( 2, 'c' ), ( 2, 'd' ),
( 3, 'a' ), ( 3, 'b' ), ( 3, 'c' ), ( 3, 'd' ),
( 4, 'a' ), ( 4, 'c' ), ( 4, 'd' ),
( 5, 'a' ), ( 5, 'a' ), ( 5, 'b' ), -- Duplicate 'a's.
( 6, 'a' ), ( 6, 'a' ) -- Duplicate 'a's.
select distinct L.id
from #table1 as L left outer join
#table1 as R on R.id = L.id and R.value = 'b'
where R.id is NULL and L.value = 'a'

Interesting SQL issue

I have a SQL problem I am trying to digest. I am using SQL Server 2005.
In a table I have data as such:
ID Type
1 A
2 A
3 A
3 B
4 B
I need to find all of the IDs that have a Type of both A and B.
Use the INTERSECT operator:
SELECT DISTINCT ID FROM [Table] WHERE Type = 'A'
INTERSECT
SELECT DISTINCT ID FROM [Table] WHERE Type = 'B'
select distinct a.id
from table a
join table b on a.id=b.id
where a.type='A'
and b.type='B';
With a semi-join (no sorting, only index seek on B):
select a.id from table a
where a.type = 'A'
and exists (select * from table b where a.id = b.id and b.type = 'B')
If you want to abstract the problem a little bit and find cases where rows with the same id contain different values in the type column, you can check for <> like this:
DECLARE #TestTable TABLE (thisid int, thisval varchar(1))
INSERT INTO #TestTable VALUES (1, 'A')
INSERT INTO #TestTable VALUES (2, 'A')
INSERT INTO #TestTable VALUES (3, 'A')
INSERT INTO #TestTable VALUES (3, 'B')
INSERT INTO #TestTable VALUES (4, 'B')
SELECT DISTINCT thisid
FROM #TestTable a
WHERE EXISTS
( SELECT *
FROM #TestTable b
WHERE a.thisid=b.thisid AND a.thisval<>b.thisval)
-- www.caliberwebgroup.com
This returns:
3
select id, count(type = 'A') as a_count, count(type = 'B') as b_count
from your_table
group by 1
having a_count > 0 and b_count > 0;
At least, this works in sane SQL environments. Dunno if it works in yours.
I was not looking at other answers, but still posting. lol
SELECT distinct t1.ID
FROM table1 AS t1
WHERE exists
(select t2.ID from table1 t2 where t2.type="A" and t2.ID=t1.ID)
and exists
(select t3.ID from table1 t3 where t3.type="B" and t3.ID=t1.ID);
SELECT Id FROM tableX AS x, tableX AS y
WHERE x.id = y.id AND x.type = 'A' AND y.type = 'B'
This is very simple
Declare #t table([ID] INT, [Type] VARCHAR(2))
INSERT INTO #t SELECT 1, 'A' UNION ALL SELECT 2,'A' UNION ALL SELECT 3,'A'
UNION ALL SELECT 3,'B' UNION ALL SELECT 4,'B' UNION ALL SELECT 5,'A' UNION ALL SELECT 5,'A'
;WITH CTE AS
(
SELECT Rn = Row_NUMBER() OVER(PARTITION BY [ID],[TYPE] ORDER BY [ID])
,*
FROM #t
)
SELECT ID
FROM CTE
WHERE Rn =1 AND ([Type]='A' or [Type]='B')
GROUP BY [ID]
HAVING (COUNT([ID])>1)
Output:
id
3
this would help if there are "unknown" amounts of types and you want to find all IDs which have all of types
select id from yourtable group by id having count(*)=(select count(distinct type) from yourtable)
select id
from idtypedata
group by id
having
sum(
case type
when 'A' then 1
when 'B' then 2
-- when 'C' then 4
-- when 'D' then 8
end
) & 1 = 1
And
sum(
case type
when 'A' then 1
when 'B' then 2
-- when 'C' then 4
-- when 'D' then 8
end
) & 2 = 2