Presto SQL group by COL1 and concat COL2 values - sql

Say I have this
col_1 | col_2
------------
1 | a
1 | b
1 | c
2 | d
2 | e
I want result like this
col_1 | col_2_concat
-------------------
1 | a,b,c
2 | d,e
something like this I would guess:
select
col_1,
join_by_comma(col2)
from tbl
group by col_1

I think you need something like:
WITH x AS (
SELECT
'a' AS c,
'b' AS c2
UNION ALL
SELECT
'a',
'b2'
UNION ALL
SELECT
'a2',
'b3'
UNION ALL
SELECT
'a2',
'b4'
)
SELECT
c,
ARRAY_JOIN(ARRAY_AGG(c2), ',') as c2
FROM x
GROUP BY
c

Related

Select rows when a value appears multiple times

I have a table like this one:
+------+------+
| ID | Cust |
+------+------+
| 1 | A |
| 1 | A |
| 1 | B |
| 1 | B |
| 2 | A |
| 2 | A |
| 2 | A |
| 2 | B |
| 3 | A |
| 3 | B |
| 3 | B |
+------+------+
I would like to get the IDs that have at least two times A and two times B. So in my example, the query should return only the ID 1,
Thanks!
In MySQL:
SELECT id
FROM test
GROUP BY id
HAVING GROUP_CONCAT(cust ORDER BY cust SEPARATOR '') LIKE '%aa%bb%'
In Oracle
WITH cte AS ( SELECT id, LISTAGG(cust, '') WITHIN GROUP (ORDER BY cust) custs
FROM test
GROUP BY id )
SELECT id
FROM cte
WHERE custs LIKE '%aa%bb%'
I would just use two levels of aggregation:
select id
from (select id, cust, count(*) as cnt
from t
where cust in ('A', 'B')
group by id, cust
) ic
group by id
having count(*) = 2 and -- both customers are in the result set
min(cnt) >= 2 -- and there are at least two instances
This is one option; lines #1 - 13 represent sample data. Query you might be interested in begins at line #14.
SQL> with test (id, cust) as
2 (select 1, 'a' from dual union all
3 select 1, 'a' from dual union all
4 select 1, 'b' from dual union all
5 select 1, 'b' from dual union all
6 select 2, 'a' from dual union all
7 select 2, 'a' from dual union all
8 select 2, 'a' from dual union all
9 select 2, 'b' from dual union all
10 select 3, 'a' from dual union all
11 select 3, 'b' from dual union all
12 select 3, 'b' from dual
13 )
14 select id
15 from (select
16 id,
17 sum(case when cust = 'a' then 1 else 0 end) suma,
18 sum(case when cust = 'b' then 1 else 0 end) sumb
19 from test
20 group by id
21 )
22 where suma = 2
23 and sumb = 2;
ID
----------
1
SQL>
You can use group by and having for the relevant Cust ('A' , 'B')
And query twice (I chose to use with to avoid multiple selects and to cache it)
with more_than_2 as
(
select Id, Cust, count(*) c
from tab
where Cust in ('A', 'B')
group by Id, Cust
having count(*) >= 2
)
select *
from tab
where exists ( select 1 from more_than_2 where more_than_2.Id = tab.Id and more_than_2.Cust = 'A')
and exists ( select 1 from more_than_2 where more_than_2.Id = tab.Id and more_than_2.Cust = 'B')
What you want is a perfect candidate for match_recognize. Here you go:
select id_ as id from t
match_recognize
(
order by id, cust
measures id as id_
pattern (A {2, } B {2, })
define A as cust = 'A',
B as cust = 'B'
)
Output:
Regards,
Ranagal

order columns by their value

I've got a table A with 3 columns that contains the same data, for exemple:
TABLE A
KEY COL1 COL2 COL3
1 A B C
2 B C null
3 A null null
4 D E F
5 null C B
6 B C A
7 D E F
As a result I expect the distinct values of this table and the order doesn't matter. So key 1 and 6 are the same and 2 and 5 also and 4 and 7. The rest is different.
Ofcourse, I can't use a distinct in my select that will only filter 4 and 7.
I could use a very complex case statement, or a select in a select with an order by. But this needs to be used in a conversion, so performance is an issue here.
Does anyone have a good performant way to do this?
The result I expect
COL1 COL2 COL3
A B C
B C null
A null null
D E F
If you can have many columns then you can UNPIVOT then order the values and then PIVOT and take the DISTINCT rows:
Oracle Setup:
CREATE TABLE table_name ( KEY, COL1, COL2, COL3 ) AS
SELECT 1, 'A', 'B', 'C' FROM DUAL UNION ALL
SELECT 2, 'B', 'C', null FROM DUAL UNION ALL
SELECT 3, 'A', null, null FROM DUAL UNION ALL
SELECT 4, 'D', 'E', 'F' FROM DUAL UNION ALL
SELECT 5, null, 'C', 'B' FROM DUAL UNION ALL
SELECT 6, 'B', 'C', 'A' FROM DUAL UNION ALL
SELECT 7, 'D', 'E', 'F' FROM DUAL
Query:
SELECT DISTINCT
COL1, COL2, COL3
FROM (
SELECT key,
value,
ROW_NUMBER() OVER ( PARTITION BY key ORDER BY value ) AS rn
FROM table_name
UNPIVOT ( value FOR name IN ( COL1, COL2, COL3 ) ) u
)
PIVOT ( MAX( value ) FOR rn IN (
1 AS COL1,
2 AS COL2,
3 AS COL3
) )
Output:
COL1 | COL2 | COL3
:--- | :--- | :---
A | B | C
B | C | null
D | E | F
A | null | null
db<>fiddle here
The complicated case expression is going to have the best performance. But the simplest method is going to be conditional aggregation:
select key,
max(case when seqnum = 1 then col end) as col1,
max(case when seqnum = 2 then col end) as col2,
max(case when seqnum = 3 then col end) as col3
from (select key,col,
row_number() over (partition by key order by col asc) as seqnum
from ((select key, col1 as col from t) union all
(select key, col2 as col from t) union all
(select key, col3 as col from t)
) kc
where col is not null
) kc
group by key;

How to exclude certain rows from sql select

How do I exclude certain rows?
For example, I have the following table:
+------+------+------+
| Col1 | Col2 | Col3 |
+------+------+------+
| 1 | 1 | R |
| 1 | 2 | D |
| 2 | 3 | R |
| 2 | 4 | R |
| 3 | 5 | R |
| 4 | 6 | D |
+------+------+------+
I need to select only:
| 2 | 3 | R |
| 2 | 4 | R |
| 3 | 5 | R |
My select that does not work properly:
with t (c1,c2,c3) as(
select 1 , 1 , 'R' from dual union all
select 1 , 2 , 'D' from dual union all
select 2 , 3 , 'R' from dual union all
select 2 , 4 , 'R' from dual union all
select 3 , 5 , 'R' from dual union all
select 4 , 6 , 'D' from dual),
tt as (select t.*,count(*) over (partition by c1) cc from t ) select * from tt where cc=1 and c3='R';
Thanks in advance!
select * from table where col2 = 'R'
or if you want to exclude rows with D value just
select * from table where col2 != 'D'
It depends on your requirements but you can do in this way:
SELECT * FROM `table` WHERE col1 = 2 AND col3 = "R"
if you want to exclude just do it like WHERE col1 != 1
You ca also use IN clause also e.g.
SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1, value2, ...);
This syntax is for MySql, but you can modify it as per your requirement or database you are using.
this will work :
select * from (select * from table_name) where rownum<=4
minus
select * from ( select * from table_name) where rownum<=2
My guess is that you want all rows for a col1 where no row for a col1 = D and at least 1 row for a col1 = R. # where [not] exists may do
DROP TABLE T;
CREATE TABLE T
(Col1 NUMBER, Col2 NUMBER, Col3 VARCHAR(1));
INSERT INTO T VALUES ( 1 , 1 , 'R');
INSERT INTO T VALUES ( 1 , 2 , 'D');
INSERT INTO T VALUES ( 2 , 3 , 'R');
INSERT INTO T VALUES ( 2 , 4 , 'R');
INSERT INTO T VALUES ( 3 , 5 , 'R');
INSERT INTO T VALUES ( 3 , 6 , 'D');
INSERT INTO T VALUES ( 4 , 5 , 'X');
INSERT INTO T VALUES ( 4 , 6 , 'Y');
INSERT INTO T VALUES ( 5 , 6 , 'X');
INSERT INTO T VALUES ( 5 , 5 , 'R');
INSERT INTO T VALUES ( 5 , 6 , 'Y');
SELECT *
FROM T
WHERE NOT EXISTS(SELECT 1 FROM T T1 WHERE T1.COL1 = T.COL1 AND COL3 = 'D') AND
EXISTS(SELECT 1 FROM T T1 WHERE T1.COL1 = T.COL1 AND COL3 = 'R');
Result
COL1 COL2 COL3
---------- ---------- ----
5 6 X
5 5 R
5 6 Y
2 3 R
2 4 R
use row_number() window function
with t (c1,c2,c3) as(
select 1 , 1 , 'R' from dual union all
select 1 , 2 , 'D' from dual union all
select 2 , 3 , 'R' from dual union all
select 2 , 4 , 'R' from dual union all
select 3 , 5 , 'R' from dual union all
select 4 , 6 , 'D' from dual
),
t1 as
(
select c1,c2,c3,row_number() over(order by c2) rn from t
) select * from t1 where t1.rn>=3 and t1.rn<=5
demo link
C1 C2 C3
2 3 R
2 4 R
3 5 R
You can try using correlated subquery
select * from tablename a
from
where exists (select 1 tablename b where a.col1=b.col1 having count(*)>1)
Based on what you have provided I can only surmise that the only requirement is for COL1 to be equal to 2 or 3 in that case all you have to do is (assuming that you actually have table);
SELECT * FROM <table_name>
WHERE col1 IN (2,3);
This will give you the desired output for the particular example provided in the question. If there is a selection requirement that goes beyond retrieving data where column 1 is either 2 or 3 than a more specific or precise answer can be provided.

SQL query for matrix results

I have the following data:
Key1 | Key2 | KValue
=====+======+=======
A | X | 100
B | Y | 200
C | X | 220
B | X | 300
B | Y | 50
I know in advance that Key1 can have the following values ['A','B','C','D'] and Key2 values ['X','Y'].
I need the SQL which will return the matrix of all Key1 x Key2 combinations with sum of their values (the combination with zero sum too). So the result from previous thata should return this:
Key1 | Key2 | KSUM
=====+======+======
A | X | 100
A | Y | 0
B | X | 300
B | Y | 250
C | X | 220
C | Y | 0
D | X | 0
D | Y | 0
GROUP BY Key1,Key2 will not return zero sum rows!
with
table1 ( key1 ) as (
select 'A' from dual union all
select 'B' from dual union all
select 'C' from dual union all
select 'D' from dual
),
table2 ( key2 ) as (
select 'X' from dual union all
select 'Y' from dual
),
test_data ( key1, key2, kvalue ) as (
select 'A', 'X', 100 from dual union all
select 'B', 'Y', 200 from dual union all
select 'C', 'X', 220 from dual union all
select 'B', 'X', 300 from dual union all
select 'B', 'Y', 50 from dual
)
-- End of simulated inputs (for testing purposes only, not part of the solution).
-- SQL query begins BELOW THIS LINE.
select t1.key1, t2.key2, nvl(ksum, 0) as ksum
from table1 t1
cross join
table2 t2
left join
( select key1, key2, sum(kvalue) as ksum
from test_data
group by key1, key2
) t3
on t1.key1 = t3.key1 and t2.key2 = t3.key2
order by key1, key2
;
Output:
KEY1 KEY2 KSUM
---- ---- ----
A X 100
A Y 0
B X 300
B Y 250
C X 220
C Y 0
D X 0
D Y 0
Get all possible combinations of Key1 and Key2 (you may store these or generate them with a Cartesian join), and left join that to an aggregate sum() query that is grouped by Key1 and Key2, coalescing the null sums to zero.

How can I make a one row from 1 group using oracle statement

There is a group as below.
refno | col1 | col2
---------------------
1 | a | aa
1 | b | bb
1 | c | cc
1 | d | dd
I want to make it like this using Oracle SQL:
refno a b c d
1 aa bb cc dd
How to do it?
You can do it using PIVOT
WITH sel AS
(SELECT 1 refno , 'a' col1 , 'aa' col2 FROM dual
UNION ALL
SELECT 1 , 'b' , 'bb' FROM dual
UNION ALL
SELECT 1 , 'c' , 'cc' FROM dual
UNION ALL
SELECT 1 , 'd' , 'dd' FROM dual
)
SELECT *
FROM sel
PIVOT
(max(col2)
FOR col1 IN ('a','b','c','d'))
But you have to specify col1 values manually - FOR col1 IN ('a','b','c','d')
More - here and here