SQL query to group multiple values - sql

Can you let me how to build sql query for the below requirement:
I have 2 tables:
Table A:
col1 | Col2
------------
1 | a
2 | b
Table B:
Col1 | Col2
-----------
1 | 10
2 | 20
a | 30
b | 40
I need output like this:
Col1 | Col2
------------
1,a | 10,30
2,b | 20,40
Can anyone one help me please. Thanks

You can use this:
SELECT CONCAT(t1.col1, ' , ', t2.col1) AS Col1, CONCAT(t1.col2, ' , ', t2.col2) AS Col2
from
(select t1.col1, t2.col2 from tableA t1, tableB t2
where t1.col1 = t2.col1) as t1,
(select t1.col1 as col3, t2.col1, t2.col2 from tableA t1, tableB t2
where t1.col2 = t2.col1) as t2
where t1.col1 = t2.col3;
SQL Fiddle here:
http://sqlfiddle.com/#!9/459ae69/25

SQL DEMO
SELECT CONCAT(CONCAT(A."Col1", ' , '), A."Col2") AS Col1,
LISTAGG(B."Col2", ', ') WITHIN GROUP (ORDER BY B."Col1") Col2
FROM TableA A
JOIN TableB B
ON A."Col1" = B."Col1"
OR A."Col2" = B."Col1"
GROUP BY CONCAT(CONCAT(A."Col1", ' , '), A."Col2")
;
OUTPUT
| COL1 | COL2 |
|-------|--------|
| 1 , a | 10, 30 |
| 2 , b | 20, 40 |

Related

SQL - Pick first row satisfying a condition below every row satisfying another condition

Suppose I have this table, named Table
+----+------+------+
| ID | Col1 | Col2 |
+----+------+------+
| 1 | A | 0 |
| 2 | B | 0 |
| 3 | C | 1 |
| 4 | A | 1 |
| 5 | D | 0 |
| 6 | A | 0 |
| 7 | F | 1 |
| 8 | H | 1 |
+----+------+------+
I want this result:
+----+------+------+
| ID | Col1 | Col2 |
+----+------+------+
| 3 | C | 1 |
| 4 | A | 1 |
| 7 | F | 1 |
+----+------+------+
That is:
If Col1 = A and Col2 = 1, take the corresponding row
If Col1 = A and Col2 = 0, take the first row below it where Col2 = 1
I tried something like
SELECT CASE
WHEN t.Col2 > 0
THEN t.Col2
WHEN t1.Col2 > 0
THEN t1.Col2
WHEN t2.Col2 > 0
THEN t2.Col2
...
FROM Table t
JOIN table t1 ON t.id - 1 = t1.id
JOIN table t2 ON t.id - 2 = t2.id
...
WHERE t.Col2 = 'A'
but it's not quite what I was looking for.
I couldn't come up with any solution. What should I do?
Use window functions SUM() and MIN():
with
cte1 as (
select *, sum(case when col1 = 'A' and col2 = 0 then 1 else 0 end) over (order by id) grp
from tablename
),
cte2 as (
select *, min(case when col2 = 1 then id end) over (partition by grp order by id) next_id
from cte1
)
select id, col1, col2
from cte2
where (col1 = 'A' and col2 = 1) or (id = next_id)
See the demo.
Results:
> id | col1 | col2
> -: | :--- | ---:
> 3 | C | 1
> 4 | A | 1
> 7 | F | 1
Hmmm . . . I am thinking lag():
select t.*
from (select t.*,
lag(col1) over (order by id) as prev_col1,
lag(col2) over (order by id) as pev_col2
from t
) t
where col1 = 'A' and col2 = 1 or
(pev_col1 = 'A' and prev_col2 = 0);
Here is a query that finds all such rows you asked for where either it's Col1=A and Col2=1 or it's the first Col2=1 following a Col1=A and Col2=0.
A brief explanation is the query only considers rows where Col2=1. It takes the row if Col1=A of course. But it also takes the row if it looks back to find the closest previous row with Col2=1 as well as the closest previous row where Col1=A and Col2=0 and it finds former is further back than the latter (or the former doesn't exist).
create table MyTable (
ID int not null identity(1,1),
Col1 varchar(100) not null,
Col2 varchar(100) not null
);
insert MyTable (Col1, Col2) values ('A', '0');
insert MyTable (Col1, Col2) values ('B', '0');
insert MyTable (Col1, Col2) values ('C', '1');
insert MyTable (Col1, Col2) values ('A', '1');
insert MyTable (Col1, Col2) values ('D', '0');
insert MyTable (Col1, Col2) values ('A', '0');
insert MyTable (Col1, Col2) values ('F', '1');
insert MyTable (Col1, Col2) values ('H', '1');
select * from MyTable;
select *
from MyTable as t
where t.Col2 = 1
and (t.Col1 = 'A'
or isnull((select top (1) t2.ID
from MyTable as t2
where t2.ID < t.ID
and t2.Col2 = 1
order by t2.ID desc
), 0)
<
(select top (1) t2.ID
from MyTable as t2
where t2.ID < t.ID
and t2.Col1 = 'A'
and t2.Col2 = 0
order by t2.ID desc
)
)
order by t.ID;

Query different number of records for each category in SQL

I have a table that looks like the following:
col1 | col2 | col3 | col4
A | 1 | 2 | 4
A | 2 | 5 | 3
A | 5 | 1 | 6
B | 3 | 1 | 2
B | 4 | 4 | 4
I have another table where the records are unique and looks like the following:
col1 | col2
A | 2
B | 1
I want to query Table 1 in such a way that I filter out only n number of records for each category in Table 1 based on the value the categories have in Table 2.
Based on Table 2 I need to extract 2 records for A and 1 record for B. I need the resulting queried table to look like the following:
col1 | col2 | col3 | col4
A | 2 | 5 | 3
A | 1 | 2 | 4
B | 3 | 1 | 2
The choice of the records are made based on col4 sorted in ascending order. I am currently tring to do this on BigQuery.
You can use row_number() and join:
select t1.col1, t1.col2, t1.col3, t1.col4
from (select t1.*, row_number() over (partition by col1 order by col4) as seqnum
from table1 t1
) t1 join
table2 t2
on t2.col1 = t1.col1 and t1.seqnum <= t2.col2
order by t1.col1, t1.col4;
Below is for BigQuery Standard SQL
#standardSQL
SELECT t.*
FROM (
SELECT ARRAY_AGG(t1 ORDER BY t1.col4) arr, MIN(t2.col2) cnt
FROM table1 t1 JOIN table2 t2 ON t1.col1 = t2.col1
GROUP BY t1.col1
), UNNEST(arr) t WITH OFFSET num
WHERE num < cnt
you can test / play with it using dummy data from your question as below
#standardSQL
WITH `table1` AS (
SELECT 'A' col1, 1 col2, 2 col3, 4 col4 UNION ALL
SELECT 'A', 2, 5, 3 UNION ALL
SELECT 'A', 5, 1, 6 UNION ALL
SELECT 'B', 3, 1, 2 UNION ALL
SELECT 'B', 4, 4, 4
), `table2` AS (
SELECT 'A' col1, 2 col2 UNION ALL
SELECT 'B', 1
)
SELECT t.*
FROM (
SELECT ARRAY_AGG(t1 ORDER BY t1.col4) arr, MIN(t2.col2) cnt
FROM table1 t1 JOIN table2 t2 ON t1.col1 = t2.col1
GROUP BY t1.col1
), UNNEST(arr) t WITH OFFSET num
WHERE num < cnt
with output as
Row col1 col2 col3 col4
1 A 2 5 3
2 A 1 2 4
3 B 3 1 2

SQL Join with 2 columns when one of them is NULL/Blank

Let's say I have 2 tables like this:
I want to join both #tmp1 & #tmp2 for Columns : Col2 & Col3 . But if the value of either of these columns is blank or Null, I want to ignore that column and just look at one.
So for example here, when I join where Col2 is ABC, I should get both DEF & Blank for Col3.
I hope I'm making some sense here. Apologize if it's not clear enough.
I can't tell from your example what columns you want to join but to solve the "null" problem you do it like this
SELECT *
FROM T1
JOIN T2 ON COALESCE(T1.COL1,T2.COL1) = T2.COL1
AND COALESCE(T1.COL2,T2.COL2) = T2.COL2
If T1.COL1 or T1.COL2 are null it will use the value of the table it is joining to. This allows null to be a "wildcard".
or if T2 is the table with nulls
SELECT *
FROM T1
JOIN T2 ON T1.COL1 = COALESCE(T2.COL1,T1.COL1)
AND T1.COL2 = COALESCE(T2.COL2,T1.COL2)
You can use union for this checking if at least 1 of the 2 columns is the same using exists:
select col1, col2, col3
from tmp1 t1
where exists (select 1
from tmp2 t2
where t1.col2 = t2.col2 or t1.col3 = t2.col3)
union
select col1, col2, col3
from tmp2 t2
where exists (select 1
from tmp1 t1
where t1.col2 = t2.col2 or t1.col3 = t2.col3)
Fiddle Demo
Results:
| col1 | col2 | col3 |
|-------|------|------|
| test1 | abc | def |
| test2 | aaa | bbb |
| test1 | abc | |
| test2 | ccc | bbb |

SQL left outer join to a min value of right table

I have two tables, lets say table1 and table2.
table1 || table2
--------||-------------
col1 || col1 | col2
--------||------|------
a || a | 4
b || a | 2
c || a | 5
d || b | 1
|| b | 3
|| d | 6
With SELECT table1.col1, table2.col2 FROM table1 LEFT OUTER JOIN table2 ON table1.col1 = table2.col1 I get following:
table1.col1 | table2.col2
-------------|-------------
a | 4
a | 2
a | 5
b | 1
b | 3
c | NULL
d | 6
How is it possible to achieve this (only get the minimum of table2.col2 so that there's no entry of table1.col1 more than once):
table1.col1 | table2.col2
-------------|-------------
a | 2
b | 1
c | NULL
d | 6
Or is it a wrong approach?
You need to use MIN:
SELECT
t1.col1,
MIN(t2.col2) AS col2
FROM table1 t1
LEFT JOIN table2 t2
ON t2.col1 = t1.col1
GROUP BY t1.col1
SQL Fiddle
Alternative solution, use a correlated sub-query:
select col1, (select min(col2) from table2 t2 where t2.col1 = t1.col1)
from table1 t1
If there are more columns in table2 you may want to use APPLY operator:
SELECT * FROM table1
OUTER APPLY(SELECT TOP 1 * FROM table2
WHERE table1.col1 = table2.col1 ORDER BY table2.col2)oa

Comparing two tables with SQL

I need a query to produce a compare table between two tables.
Something like this:
table_1:
col1 | col2 | col3
a | 1 | a_comment
b | 2 | b_comment
table_2:
col1 | col2 | col3
a | 3 | a_comment
c | 4 | c_comment
Query result:
col1 | table_1.col2 | table_2.col2 | col3
a | 1 | 3 | a_comment
b | 2 | NULL | b_comment
c | NULL | 4 | c_comment
Also, I need to keep the order, s.t. if x in col1 is before y in col1 in any of the tables it will also be before it in the query result.
I tried to do it with FULL JOIN but it duplicates col1 and col3.
Thank you!
select t1.col1, t1.col2, t2.col2, t1.col3
from table_1 t1
left join table_2 t2
on t1.col1 = t2.col1
and t1.col3 = t2.col3
union
select t2.col1, t2.col2, t1.col2, t2.col3
from table_2 t2
left join table_1 t1
on t2.col1 = t1.col1
and t2.col3 = t1.col3
Full join is ok i think, you just have to select not all but just what you want, like
SELECT ISNULL(table_1.col1, table_2.col1) col1, table_1.col2, table2.col2, ISNULL(table_1.col3, table_2.col3) col3
ISNULL might be called different depending on what database system u use
SELECT col1
, t1.col2
, t2.col3
, col3
FROM table1 t1
FULL OUTER JOIN table2 t2
USING (col1, col3)