sql counting values in 3 columns - sql

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

Related

How <> works when compared with multiple values?

I have a table and sample data as below.
create table MyTable
(
Col1 NUMBER,
Col2 VARCHAR2(30)
)
MyTable
Col1 Col2
1 | Val1
2 | Val2
3 | Val3
4 | Val4
Below is the query which is already written and deployed to the application by some one else.
SELECT Col2
FROM MyTable A WHERE Col1 IN (2,3,4)
AND NOT EXISTS
(SELECT 1
FROM MyTable B
WHERE B.Col1 <> A.Col1)
How does <> compare multiple values in this case?
Does it just compare with value 2? Or randomly compares with any value amoung 2,3 or 4?
The values are compare one by one.
If you have the sample data:
CREATE TABLE MyTable(col1, col2) AS
SELECT 1, 'Val1' FROM DUAL UNION ALL
SELECT 2, 'Val2' FROM DUAL UNION ALL
SELECT 3, 'Val3' FROM DUAL UNION ALL
SELECT 4, 'Val4' FROM DUAL;
Then:
SELECT *
FROM MyTable A
WHERE Col1 IN (2,3,4)
Will return 3 rows:
COL1
COL2
2
Val2
3
Val3
4
Val4
For your full query:
SELECT Col2
FROM MyTable A
WHERE Col1 IN (2,3,4)
AND NOT EXISTS(
SELECT 1
FROM MyTable B
WHERE B.Col1 <> A.Col1
)
Then for each of the rows it will check that a row does NOT EXISTS in the MyTable table where B.Col1 <> A.Col1. In your case, there are 3 rows that exist in the sub-query for each of the matched rows in the main query. You can see this with the query:
SELECT Col2,
(SELECT LISTAGG(col1, ',') WITHIN GROUP (ORDER BY col1)
FROM MyTable B
WHERE B.Col1 = A.Col1) AS equal,
(SELECT LISTAGG(col1, ',') WITHIN GROUP (ORDER BY col1)
FROM MyTable B
WHERE B.Col1 <> A.Col1) AS not_equal
FROM MyTable A
WHERE Col1 IN (2,3,4)
Which outputs:
COL2
EQUAL
NOT_EQUAL
Val2
2
1,3,4
Val3
3
1,2,4
Val4
4
1,2,3
Given that there is always (more than) one row that exists then the NOT EXISTS condition will exclude every row and your result set will be empty.
db<>fiddle here

how to extract the rows where a group appears more than a certain number of times

I have the following table
col1 col2 col3 key
A B C 1
A B B 2
A B B 3
A B D 4
B D C 5
I would like to extract the rows where the group col1, col2, col3 appears more than once in the table.
A B B 2
A B B 3
So far, I have:
SELECT col1, col2, col3, count(*)
FROM db.table
GROUP BY col1, col2, col3
HAVING count(*) > 1
col1 col2 col3 count(*)
A B B 2
Is there a way to extract those rows with A B B without having to join the final table with the initial table?
You could use exists logic:
SELECT col1, col2, col3, "key"
FROM yourTable t1
WHERE EXISTS (SELECT 1 FROM yourTable t2
WHERE t2.col1 = t1.col1 AND t2.col2 = t1.col2 AND
t2.col3 = t1.col3 AND
t2."key" <> t1."key");
Try below query with CTE
with MyCTE
as
(
select col1,col2,col3,Key,COUNT(*) over(PARTITION BY col1,col2,col3 order
by col1,col2,col3) as Duplicate from yourtable
)
select col1,col2,col3,key from MyCTE where Duplicate>1

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

Merge rows in postgresql

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;

Count on case Oracle

WE have below data in oracle database -
col1 col2
Z1 A
Z1 B
Z2 A
Z2 C
Z3 A
Z4 D
I want count on column two in such a way that -
Ouput -
col2 count
A 3 (Z1,Z2,Z3)
B 0 (Dont count if A is already present for record)
C 0
D 1 (Z4)
Best Regards
You can use window function rank() to achieve this.
select col2, count(case when rn = 1 then 1 end) cnt from (
select t.*,
rank() over (partition by col1 order by case when col2 = 'A' then 1 else 2 end) rn
from table t
) group by col2;
The most general solution to your propositions where each key COL1 is counted only in the first occurrence of the key COL2 (in alphabetical order)
WITH tab AS
(
SELECT 'Z1' col1, 'A' col2 FROM dual UNION ALL
SELECT 'Z1' col1, 'B' col2 FROM dual UNION ALL
SELECT 'Z2' col1, 'A' col2 FROM dual UNION ALL
SELECT 'Z2' col1, 'C' col2 FROM dual UNION ALL
SELECT 'Z3' col1, 'A' col2 FROM dual UNION ALL
SELECT 'Z4' col1, 'D' col2 FROM dual
), tab2 as (
select COL1, COL2,
row_number() over (partition by COL1 order by COL2) as rn
from tab)
select COL1, COL2,
case when rn = 1 then 1 else 0 end is_valid
from tab2
order by 1,2
;
COL1 COL2 IS_VALID
---- ---- ----------
Z1 A 1
Z1 B 0
Z2 A 1
Z2 C 0
Z3 A 1
Z4 D 1
The rest is simple group by with a SUM on IS_VALID
select COL2, sum(is_valid) cnt from tab3 -- TAB3 is the above row source
group by COL2
order by 1
COL2 CNT
---- ----------
A 3
B 0
C 0
D 1
Thanks Guys. But I could do this way -
select count(case
when (LISTAGG(col2,'-') WITHIN GROUP (ORDER BY col2)) like '%A%' then 1
else null
end) A,
count(case
when (LISTAGG(col2,'-') WITHIN GROUP (ORDER BY col2)) = 'B' then 1
else null
end) B,
count(case
when (LISTAGG(col2,'-') WITHIN GROUP (ORDER BY col2)) = 'C' then 1
else null
end) C,
count(case
when (LISTAGG(col2,'-') WITHIN GROUP (ORDER BY col2)) = 'D' then 1
else null
end) D
from T
GROUP BY col1
Thanks for your replies
Assume your table name is table_name, One way to do it is using this:
WITH table_a AS
(
SELECT DISTINCT col1
FROM table_name
WHERE col2 = 'A'
)
SELECT col2,
SUM(CASE WHEN col1 IN (SELECT col1 FROM table_a)
THEN DECODE(col2, 'A', 1, 0)
ELSE 1 END
) count
FROM table_name
GROUP BY col2
ORDER BY col2;
Tested ok:
WITH table_name AS
(
SELECT 'Z1' col1, 'A' col2 FROM dual UNION ALL
SELECT 'Z1' col1, 'B' col2 FROM dual UNION ALL
SELECT 'Z2' col1, 'A' col2 FROM dual UNION ALL
SELECT 'Z2' col1, 'C' col2 FROM dual UNION ALL
SELECT 'Z3' col1, 'A' col2 FROM dual UNION ALL
--SELECT 'Z4' col1, 'B' col2 FROM dual UNION ALL
SELECT 'Z4' col1, 'D' col2 FROM dual
)
, table_a AS
(
SELECT DISTINCT col1
FROM table_name
WHERE col2 = 'A'
)
SELECT col2,
SUM(CASE WHEN col1 IN (SELECT col1 FROM table_a)
THEN DECODE(col2, 'A', 1, 0)
ELSE 1 END
) count
FROM table_name
GROUP BY col2
ORDER BY col2;
You want to count each record where either col2 is 'A' or no 'A' record exists for col1.
select
col2,
count(
case
when col2 = 'A' or col1 not in (select col1 from table_name where col2 = 'A') then 1
end) as cnt
from table_name
group by col2;
select col2, count(case when col2 = col3 then 'x' end) as ct
from ( select col2, min(col2) over (partition by col1) as col3
from your_table
)
group by col2
order by col2 -- if needed
;
Explanation:
There is an inner query (a.k.a. "subquery") which returns one row for each row in the original table. It returns col2 as is, and an additional (new) column, labeled col3. col3 is calculated as the "first" or min() value of col2 (in alphabetical order) for all the rows in the original table that have the same value in col1 as the current row does. This is a typical example of an analytic function; partition by col1 is similar to group by col1 but it returns all the rows in the group (all the original rows from the original table) instead of one row per group, as would an aggregate function.
To see what the inner query does by itself, select it and run it in your favorite front-end. You may add col1 to the select in the inner query - that will make what's going on in this query even clearer. You'll get the initial table, with one more column, col3, that shows the "min" col2 for each value of col1. I didn't include col1 in the subquery because I don't need it, but add it back to see what the subquery really does.
Then in the outer query I take the results from the inner query and I group by col2. For each col2 I count just how many times it is equal to the "min" value of col2 for the corresponding col1 value. That's what the case expression does in the count() function; when col2 is not equal to col3, then case returns null (by default) so the expression - and therefore the row - is not counted.
I should add that the query written this way assumes there are no duplicate (col1, col2) rows in the original table. If there are, then the inner subquery should select from a sub-subquery; line 3 of my code should be
from (select distinct col1, col2 from your_table)
Use the below script:
SELECT A.COL2, NVL(B.CNT, 0) AS CNT
FROM (SELECT DISTINCT COL2 FROM TET) A
LEFT JOIN (SELECT COL2, COUNT(COL2) AS CNT
FROM (SELECT SUBSTR(F, 1, INSTR(F, ',') - 1) AS COL2,
ROW_NUMBER() OVER(PARTITION BY SUBSTR(F, 1, INSTR(F, ',') - 1) ORDER BY SUBSTR(F, 1, INSTR(F, ',') - 1)) AS U
FROM (SELECT COL1,
LISTAGG(COL2, ',') WITHIN GROUP(ORDER BY COL2) || ',' AS F
FROM TET
GROUP BY COL1)) A
GROUP BY COL2) B
ON A.COL2 = B.COL2
ORDER BY A.COL2;