Merge rows in postgresql - sql

I have a table with the following values in columns:
Table (col1, col2, col3, col4, col5, col6):
a b c d e f
a b c g h i
a b c k l m
a b c n o p
As a result I want to have one row:
a b c d e f g h i k l m n o p
How to do that?

use union and string_agg
select string_agg(distinct c ,' ') from
(
select col1 as c from t
union
select col2 from t
union
select col3 from t
union
select col4 from t
union
select col5 from t
union
select col6 from t
) as t1

I would use string_agg() :
select string_agg(t.col, ' ')
from (select col1 as col
from t
union
select col2
from t
union
. . .
select col6
from t
) t;

Related

Filter in SQL on distinct values after grouping

I have a dataset like
col1 col2 col3
A x 1
A x 2
A x 3
B y 4
B -y 5
B y 6
C -z 7
C z 8
C -z 9
D t 10
D t 11
D t 12
how can i pick out just the groups from col1 that have distinct values in col2? So A,D in this case.
something like
select * from table t1
where (select count(distinct col2)
from table t2
where t1.col1 = t2.col1) > 1
but more optimized?
If all you need is the column col1 you can group by col1 and set the condition in the HAVING clause:
SELECT col1
FROM tablename
GROUP BY col1
HAVING COUNT(DISTINCT col2) = 1;
If you want all the rows from the table use the above query with the operator IN:
SELECT *
FROM tablename
WHERE col1 IN (
SELECT col1
FROM tablename
GROUP BY col1
HAVING COUNT(DISTINCT col2) = 1
)
You can use group by and having:
select col1
from t
group by col1
having min(col2) <> max(col2);

Unique pairs of columns SQL

I have 4 columns, like below:
COL1 COL1_TIME COL2 COL2_TIME
A 09:20:00 E 09:35:00
A 09:20:00 F 09:36:00
A 09:20:00 G 09:40:00
A 09:20:00 H 09:59:00
B 09:25:00 E 09:35:00
B 09:25:00 F 09:36:00
B 09:25:00 G 09:40:00
B 09:25:00 H 09:59:00
C 09:30:00 E 09:35:00
C 09:30:00 F 09:36:00
C 09:30:00 G 09:40:00
C 09:30:00 H 09:59:00
D 09:50:00 H 09:59:00
I have to select unique pairs of values from columns COL1 and COL2. To find a pair, you should take Closest time to COL1_TIME in COL2_TIME.
So the colsest time for A is E. For B its F - E is taken already etc.
Result should look like this:
A E
B F
C G
D H
Any ideas?
Well, without the recursive WITH Common Table Expression, you need to hard-wire a few things.
And if you have more than 4 values for COL1, it gets even more tedious; and if it is about a very important business issue, consider writing a UDx for that.
But - otherwise - here is one that works - input included in the first Common Table Expression of the WITH clause:
WITH
input(col1,col1_time,col2,col2_time) AS (
SELECT 'A',TIME '09:20:00','E',TIME '09:35:00'
UNION ALL SELECT 'A',TIME '09:20:00','F',TIME '09:36:00'
UNION ALL SELECT 'A',TIME '09:20:00','G',TIME '09:40:00'
UNION ALL SELECT 'A',TIME '09:20:00','H',TIME '09:59:00'
UNION ALL SELECT 'B',TIME '09:25:00','E',TIME '09:35:00'
UNION ALL SELECT 'B',TIME '09:25:00','F',TIME '09:36:00'
UNION ALL SELECT 'B',TIME '09:25:00','G',TIME '09:40:00'
UNION ALL SELECT 'B',TIME '09:25:00','H',TIME '09:59:00'
UNION ALL SELECT 'C',TIME '09:30:00','E',TIME '09:35:00'
UNION ALL SELECT 'C',TIME '09:30:00','F',TIME '09:36:00'
UNION ALL SELECT 'C',TIME '09:30:00','G',TIME '09:40:00'
UNION ALL SELECT 'C',TIME '09:30:00','H',TIME '09:59:00'
UNION ALL SELECT 'D',TIME '09:50:00','H',TIME '09:59:00'
)
,
col1_A AS (
SELECT
col1
, col2
FROM input
WHERE col1='A'
ORDER BY ABS(TIMESTAMPDIFF('SECOND',col1_time::TIMESTAMP,col2_time::TIMESTAMP))
LIMIT 1
)
,
col1_B AS (
SELECT
col1
, col2
FROM input
WHERE col1='B'
AND col2 NOT IN (
SELECT col2 FROM col1_A
)
ORDER BY ABS(TIMESTAMPDIFF('SECOND',col1_time::TIMESTAMP,col2_time::TIMESTAMP))
LIMIT 1
)
,
col1_C AS (
SELECT
col1
, col2
FROM input
WHERE col1='C'
AND col2 NOT IN (
SELECT col2 FROM col1_A
UNION ALL SELECT col2 FROM col1_B
)
ORDER BY ABS(TIMESTAMPDIFF('SECOND',col1_time::TIMESTAMP,col2_time::TIMESTAMP))
LIMIT 1
)
,
col1_D AS (
SELECT
col1
, col2
FROM input
WHERE col1='D'
AND col2 NOT IN (
SELECT col2 FROM col1_A
UNION ALL SELECT col2 FROM col1_B
UNION ALL SELECT col2 FROM col1_C
)
ORDER BY ABS(TIMESTAMPDIFF('SECOND',col1_time::TIMESTAMP,col2_time::TIMESTAMP))
LIMIT 1
)
SELECT * FROM col1_A
UNION ALL SELECT * FROM col1_B
UNION ALL SELECT * FROM col1_C
UNION ALL SELECT * FROM col1_D
;
If it's not what you'd hoped for, I'd not be surprised ...
Happy playing ...
Marco the Sane
If the cardinality of COL1 and COL2 distinct values is always 1-1 and no other special occasions/exeptions exist, you can do the following:
with temp1 as (
select col1
,col1_time
,row_number() over (partition by col1 order by col1 desc) as rownum1
), temp2 as(
select col2
,col2_time
,row_number() over (partition by col2 order by col2 desc) as rownum2
)
select distinct(temp1.col1)
,distinct(temp2.col2)
from temp1,temp2
where temp1.rownum1 = temp2.rownum2

sql counting values in 3 columns

How do I count all values when the possible values could be in anywhere from 0 to 3 columns. I want to add up all the a's, b's, c's, d's
COL1 COL2 COL3
a
b
b a
a c b
a b
c a
d a c
c d
select col, count(*)
from (
select col1 col from tablename
union all
select col2 from tablename
union all
select col3 from tablename) t
group by col
You can use UNPIVOT:
SELECT t.Val, COUNT(*) AS cnt
FROM (
SELECT Col1, Col2, Col3
FROM mytable
UNPIVOT (
Val FOR Col IN ("Col1", "Col2", "Col3"))) t
GROU BY t.Val

Display record even if it doesn't exist

So I have this table
Col1 Col2 Col3
A 34 X
B 43 L
A 36 L
Now if I query
select * from Table1 where col1 in ('A','B','C')
I am expecting something like
Col1 Col2 Col3
A 34 X
B 43 L
A 36 L
C - -
Is it possible ?
P.S: the - in row C are just to show that the column is empty.
You could create a nested table schema object type:
create type T_List1 as table of varchar2(100);
And then construct your query as follows:
select s.column_value as col1
, nvl(to_char(t.col2), '-') as col2
, nvl(col3, '-') as col3
from Table1 t
right join table(T_List1('A', 'B', 'C')) s
on (t.col1 = s.column_value)
Example:
-- sample of data from your question
with Table1(Col1, Col2, Col3) as(
select 'A', 34, 'X' from dual union all
select 'B', 43, 'L' from dual union all
select 'A', 36, 'L' from dual
) -- actual query
select s.column_value as col1
, nvl(to_char(t.col2), '-') as col2
, nvl(col3, '-') as col3
from Table1 t
right join table(T_List1('A', 'B', 'C')) s --< here list your values
on (t.col1 = s.column_value) -- as you would using `IN` clause
Result:
COL1 COL2 COL3
------------------------
A 36 L
A 34 X
B 43 L
C - -
SQLFiddle Demo
To do this you can use a driver table that has all the values you want returned in it ie:
col1
A
B
C
D
E
Then LEFT JOIN to your table.
SELECT *
FROM driver d
LEFT JOIN Table1 t
ON d.col1 = t.col1
WHERE d.col1 in ('A','B','C')
If you don't want to create an extra nested table type as in Nicholas Krasnov's answer or don't want to create a separate temporary table with A,B,C rows then just create a driving table with with clause:
with driving_table(col) AS
(
select 'A' from dual
union
select 'B' from dual
union
select 'C' from dual
)
select dt.col as col1
, nvl(to_char(t.col2), '-') as col2
, nvl(col3, '-') as col3
from Table1 t
right join driving_table dt
on (t.col1 = dt.col)
http://sqlfiddle.com/#!4/112ef/2

T-SQL: Include a row count as a column for unique rows

I have a table, we'll call TableA and contains the following 3 columns with data
Col1 Col2 Col3
a b c
a b c
d e f
g h i
g h i
g h i
I want to return a record set that looks like this:
Col1 Col2 Col3 Total
a b c 2
d e f 1
g h i 3
Duplicate rows are only returned once along with the count of their occurrences. Not sure how to formulate the sql. Thanks for your help!
Try this:
SELECT Col1, Col2, Col3, COUNT(*) AS Total
FROM TableA
GROUP BY Col1, Col2, Col3
use subquery .
select a1.col1
, a1.col2
, a1.col3
, (select count(*)
from tableA a2
where a2.col1=col1
and a2.col2=col2
and a2.col3=col3) as count
from tableA a1