Display 0 when count(*) is zero for a group by clause - sql

I am trying to write a SQL query which can display value as 0 if there are no rows for the specified condition
I have tried the following so far but nothing seems to work
coalesce(count(m.a),'0')
isnull(count(m.a),'0')
case when count(*) > 0 then count(*) else '0' end
select M.a, m.b, m.c, m.d, m.e,
--coalesce(count(m.a),'0') as CountOfRecords
--isnull(count(m.a),'0') as CountOfRecords
--case when count(*) > 0 then count(*) else '0' end
from my_table M
left join
(select a, b,c,d,e
from my_table
group by a, b,c,d,e
having count(*) >1 ) B
on M.b = B.b
and M.c = B.c
and M.d = B.d
and M.e = B.e
and m.a <> B.a
where M.a in (1,2)
and M.date<= '1/1/2019'
group by M.a, m.b, m.c, m.d, m.e
Expected Result
A B C D E count
1 1 1 1 1 10
2 2 2 2 2 0
Actual Result
A B C D E count
1 1 1 1 1 10

You need to use a nested request:
select coalesce(nb, 0) from (
select count(*) nb from my_table
group by my_table.a
) nested;

Are you looking for something like this?
select a, b, c, d, e,
sum(case when M.date <= '2019-01-01' then 1 else 0 end) as cnt
from my_table
where a in (1, 2)
group by a, b, c, d, e;
This keeps all rows in the original data that match the condition on a, but not necessarily the condition on the date. It then counts only the rows that match the date.

Related

How to change value if multiple duplicate rows more then 3 in Oracle SQL

I am trying to select if A.POLICY_NO or A.POLICY_TITLE values duplicate rows more than 3, want to change C.SMALL_CATEGORY_TITLE values to "Z" and also want to cahge B.SMALL_CATEGORY_SID values to null.
I used 3 tables that YIP.YOUTH_POLICY as A, YIP.YOUTH_POLICY_AREA as B and YIP.YOUTH_SMALL_CATEGORY as C. the PK and FK is POLICY_NO.
ForExample if the table
A.POLICY_NO
B.SMALL_CATEGORY_SID
C.SMALL_CATEGORY_TITLE
A.POLICY_TITLE
1
80
A
VALUE1
1
90
B
VALUE1
1
95
C
VALUE1
2
80
A
VALUE2
2
90
B
VALUE2
2
95
C
VALUE2
3
80
A
VALUE3
3
90
B
VALUE3
4
80
A
VALUE4
I wnat to select like
A.POLICY_NO
B.SMALL_CATEGORY_SID
C.SMALL_CATEGORY_TITLE
A.POLICY_TITLE
1
NULL
Z
VALUE1
2
NULL
Z
VALUE2
3
80
A
VALUE3
3
90
B
VALUE3
4
80
A
VALUE4
this is the query select except when duplicate values more then 3,
SELECT
A.POLICY_NO
, B.SMALL_CATEGORY_SID
, C.SMALL_CATEGORY_TITLE
, A.POLICY_TITLE
, COUNT(*) OVER() AS TOTAL_COUNT
FROM
YIP.YOUTH_POLICY A
LEFT JOIN
YIP.YOUTH_POLICY_AREA B
ON A.POLICY_NO = B.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY C
ON B.SMALL_CATEGORY_SID = C.SMALL_CATEGORY_SID
WHERE A.POLICY_NO IN (SELECT
F.POLICY_NO
FROM YIP.YOUTH_POLICY F
LEFT JOIN
YIP.YOUTH_POLICY_AREA G
ON F.POLICY_NO = G.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY H
ON G.SMALL_CATEGORY_SID = H.SMALL_CATEGORY_SID
GROUP BY F.POLICY_NO
HAVING COUNT(*) < 3)
ORDER BY A.POLICY_NO;
and I was tried to change C.SMALL_CATEGORY_TITLE values when POLICY_NO values duplicated more then 3
SELECT
A.POLICY_NO
--, B.SMALL_CATEGORY_SID
, SUM(CASE WHEN C.SMALL_CATEGORY_TITLE IN (SELECT
F.POLICY_NO
FROM YIP.YOUTH_POLICY F
LEFT JOIN
YIP.YOUTH_POLICY_AREA G
ON F.POLICY_NO = G.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY H
ON G.SMALL_CATEGORY_SID = H.SMALL_CATEGORY_SID
GROUP BY F.POLICY_NO
HAVING COUNT(*) > 2) THEN 1 ELSE NULL END) AS 'Z'
, A.POLICY_TITLE
, COUNT(*) OVER() AS TOTAL_COUNT
FROM
YIP.YOUTH_POLICY A
LEFT JOIN
YIP.YOUTH_POLICY_AREA B
ON A.POLICY_NO = B.POLICY_NO
LEFT JOIN
YIP.YOUTH_SMALL_CATEGORY C
ON B.SMALL_CATEGORY_SID = C.SMALL_CATEGORY_SID
ORDER BY A.POLICY_NO;
I got SQL Error [42000]: JDBC-8006:Missing FROM keyword. ¶at line 17, column 59 of null:¶ HAVING COUNT(*) > 2) THEN 1 ELSE NULL END) AS 'Z'¶
Is there any way to fix it? I was thinking more then 5 hours but i could't fix it
The trick is to use the windowing function COUNT(*) OVER to get a count over the entire rowset in order to later make decisions about each individual row. You can finally collapse it down with DISTINCT or GROUP BY.
SELECT DISTINCT
policy_no,
CASE WHEN (policy_count >= 3 OR policy_title_count>= 3) THEN NULL
ELSE small_category_sid
END AS small_category_sid,
CASE WHEN (policy_count >= 3 OR policy_title_count>= 3) THEN 'Z'
ELSE small_category_title
END AS small_category_title,
policy_title
FROM (SELECT x.*,
COUNT(*) OVER (PARTITION BY policy_no) policy_count,
COUNT(*) OVER (PARTITION BY policy_title) policy_title_count
FROM yip.youth_policy x)

PostgreSQL - Removing NULLS row and column from conditional aggregation results

I have a query for a multidimensional table using conditional aggregation
select A,
SUM(case when D = 3 then D end) as SUM_D1,
SUM(case when D = 4 then D end) as SUM_D2)
The result:
A SUM_D1 SUM_D2
-------------------
a1 100 NULL
a1 200 NULL
a3 NULL NULL
a4 NULL NULL
However, I would like to hide all NULL rows and columns as follows:
A SUM_D1
-----------
a1 100
a1 200
I have looked for similar problems but they are not my expected answer.
Any help is much appreciated,
Thank you
I think this does what you want:
select A,
coalesce(sum(case when D = 3 then D end),
sum(case when D = 4 then D end)
) as sum_d
from t
group by A
having sum(case when d in (3, 4) then 1 else 0 end) > 0;
Note that this returns only one column -- as in your example. If both "3" and "4" are in the data, then the value is for the "3"s.
If you want a query that returns a variable number of columns, then you need to use dynamic SQL -- or some other method. SQL queries return a fixed number of columns.
One method would be to return the values as an array:
select a,
array_agg(d order by d) as ds,
array_agg(sumd order by d) as sumds
from (select a, d, sum(d) as sumd
from t
where d in (3, 4)
group by a, d
) d
group by a;
To filter all-NULL rows you can use HAVING
select *
from
(
select A,
SUM(case when D = 3 then D end) as SUM_D1,
SUM(case when D = 4 then D end) as SUM_D2)
...
) as dt
where SUM_D1 is not null
and SUM_D2 is not null
Of course, if you got simple conditions like the ones in your example you better filter before aggregation:
select A,
SUM(case when D = 3 then D end) as SUM_D1,
SUM(case when D = 4 then D end) as SUM_D2)
...
where D in (3,4)
Now at least one calculation will return a value, thus no need to check for all-NULL.
To filter all-NULL columns you need some Dynamic SQL:
materialize the data in a temporary tabke using Insert/Select
scan each column for all-NULL select 1 from temp having count(SUM_D1) > 0
dynamically create the Select list based on this
run the Select
But why do you think you need this? It will be confusing for a user to run the same Stored Procedure and receive a different number of columns for each run.
I may have misinterpreted your question because the solution seems so simple:
select A,
SUM(case when D = 3 then D end) as SUM_D1,
SUM(case when D = 4 then D end) as SUM_D2)
where D is not null
This is not what you want, is it? :-)
Null appear because the condition that's not handled by case statement
select A,
SUM(case when D = 3 then D end) as SUM_D1,
SUM(case when D = 4 then D end) as SUM_D2
from
Table1
group by
A
having
(case when D = 3 or D = 4 then D end) is not null
As comment said if you want to suppress the null value.. You can use having to suppress null using is not null

sql excluding certain results

lets say i have a data set of
A B
-- --
a 1
b 1
c 1
d 1
d 2
e 1
f 1
f 2
g 1
how would i exclude a result in column B of 1, if column B has values of both 1 and 2 for the same value in column A?
i want my results to look like this
A B
-- --
a 1
b 1
c 1
d 2
e 1
f 2
g 1
Checking explicitly here for the values 1 and 2 and using the fact that there are exactly two of them. You could potentially make this less cumbersome if it's safe to assume that you always want the highest value.
select
tbl.A,
tbl.B
from
Table1 tbl
left outer join (
select
A
from
Table1
where
B in (1,2)
group by
A
having
count(B) = 2
) mlt on tbl.A = mlt.A
where
(
mlt.A is not null
and tbl.B = 2
) or (
mlt.A is null
and tbl.B = 1
)
Figure out all the A values that have both 1 and 2.
Match those to the table on the A value.
If A is in the subquery, use the B = 2 record. If it isn't, use the B = 1 record.
select
* from tbl where a IN
(
select
a from tbl
group by a
having count(*)>1
)
and b!=1
UNION ALL
select
* from tbl where a IN
(
select
a from tbl
group by a
having count(*)=1
)
For the example data and desired result, the simplest query to achieve the result would be a GROUP BY operation and an aggregate function.
SELECT d.A
, MAX(d.B) AS B
FROM my_data_set d
GROUP BY d.A
ORDER BY d.A
If we are only interested in rows that have a 1 or 2 in column B, we can add a WHERE clause
SELECT d.A
, MAX(d.B) AS B
FROM my_data_set d
WHERE d.B IN (1,2)
GROUP BY d.A
ORDER BY d.A
With the example data, the output is the same.
Both of these statements achieve the specified result. (There is only a single row returned for each distinct value in A.)
Or, for the same the example data, we can return the same result set with a more literal implementation of the specification.
To exclude rows with 1when there is a row with 2 for the same value of A, we can use a NOT EXISTS predicate and a correlated subquery.
SELECT d.A
, d.B
FROM my_data_set d
WHERE ( d.B = 2 )
OR ( d.B = 1 AND
NOT EXISTS ( SELECT 1
FROM my_data_set e
WHERE e.A = d.A
AND e.B = 2
)
)
ORDER BY d.A, d.B

SQL for set of all in group by

Assume I have table foo
A B C
==============
1 1 1
1 2 3
1 2 4
1 3 6
2 2 6
I want the set of all C where I have a duplicate AB. Something like:
select all(C) from foo group by a, b having count(b) > 1
I want the result to be
all(C)
===
3
4
Is there an easy way to do this in Oracle SQL?
SELECT t1.c
FROM foo t1
JOIN foo t2 ON (t1.a = t2.a AND
t1.b = t2.b AND
t1.rowid != t2.rowid)
should give you what you're after. A bit more efficient would likely be to use an analytic function
SELECT c
FROM (SELECT f.*,
count(*) over (partition by a, b) cnt
FROM foo f)
WHERE cnt > 1
Try this:
SELECT C
FROM
(select C, COUNT(*) OVER(PARTITION BY A, B) AS DUPLICATES
from MY_TABLE) AS RESULTS
WHERE DUPLICATES > 1
I figured it out. I used
select f.c from foo f
join (select a, b from foo group by a, b having count(c) > 1) dupes
on dupes.a = f.a and dupes.b = f.b;

Oracle SQL count colums and group by date

Hi I have a table like this one
C_DATE SOURCE
11/21/2012 A
11/22/2012 A
11/22/2012 A
11/22/2012 A
11/23/2012 A
11/23/2012 A
11/25/2012 A
11/26/2012 A
11/26/2012 B
11/26/2012 B
11/26/2012 B
11/21/2012 B
11/22/2012 B
11/22/2012 B
11/23/2012 B
11/23/2012 C
11/24/2012 C
11/24/2012 C
11/24/2012 C
11/24/2012 C
11/25/2012 C
How can I have the count by source and by date as follows:
c_date source a source b source c
11/21/2012 1 4 0
11/22/2012 1 1 1
11/23/2012 0 0 1
11/24/2012 and so on..
The closest I have got is something like
select trunc(c_date) XDATE,
(select count(**) from TABLE where source='A') A,
(select count(**) from TABLE where source='B') B,
(select count(*) from TABLE where source='C') C
from TABLE
group by trunc(C_DATE)
order by trunc(C_DATE) asc
but it repeats the total count for each row I cannot find how to relate the count colums with the date.
Thanks a lot for your help
select trunc(c_date) XDATE,
sum(case source when 'A' then 1 else 0 end) cnt_a,
sum(case source when 'B' then 1 else 0 end) cnt_b,
sum(case source when 'C' then 1 else 0 end) cnt_c,
from TABLE
group by trunc(C_DATE)
order by trunc(C_DATE) asc
update as long as you use 11g, you can use modern pivot clause :)
select xdate, a, b, c
from
(select trunc(c_date) XDATE, source, count(*)
from tab
group by trunc(c_date), source )
pivot
( count(*) for source in ('A' a, 'B' b, 'C' c) )
order by 1;
http://sqlfiddle.com/#!4/d0269/16