How to join and count with multiple condition in oracle - sql

i have 2 tables :
table1(id, name)
1233 AAA
3424 BBB
4345 CCC
4342 DDD
1243 RRR
3453 GGG
table2(id,date,status)
1233 01/07/19 1
3424 01/07/19 1
4342 01/07/19 2
1243 01/07/19 1
4342 01/07/19 1
4345 02/07/19 2
1243 02/07/19 1
1233 02/07/19 1
4345 03/07/19 1
4342 03/07/19 2
1233 03/07/19 1
4342 04/07/19 2
4345 04/07/19 2
4342 04/07/19 1
1243 04/07/19 2
15 ROW
i have tried this code
SELECT rn.id, name, NVL(cnt, 0) jum
FROM table1 rn
LEFT JOIN (SELECT id, COUNT(id) AS cnt
FROM VIEW_AKTIFITAS
WHERE extract(year from date)=2019
AND extract(month from date)=7
GROUP BY id,extract(month from date) n ON n.id= rn.id
i want to get this result
how to count the status entry in table 2
RESULT
ID COUNT_STATUS_1 COUNT_STATUS_2 TOTAL_COUNT
1233 2 0 2
3424 2 0 2
4345 1 2 3
4342 2 3 5
1243 2 1 3
3453 0 0 0
please help me to solve this problem.. thanks

Left join table2 to table1 and then use conditional aggregation to get the different counts for the two status.
SELECT t1.id,
count(CASE
WHEN t2.status = 1 THEN
1
END) count_status_1,
count(CASE
WHEN t2.status = 2 THEN
1
END) count_status_2,
count(t2.status) total_count
FROM table1 t1
LEFT JOIN table2 t2
ON t2.id = t1.id;

In Oracle 11.1 and higher, you can use the PIVOT operator to do the aggregation. Something like this:
select id, name, count_status_1, count_status_2,
count_status_1 + count_status_2 as total_count
from (select t1.id, t1.name, t2.status from table1 t1 left join table2 t2
on t1.id = t2.id)
pivot (count(*) for status in (1 as count_status_1, 2 as count_status_2))
order by id -- if needed
;

Based on your sample code, you seem to want:
SELECT rn.id, rn.name, NVL(cnt, 0) jum,
SUM(CASE WHEN a.status = 1 THEN 1 ELSE 0 END) as status_1,
SUM(CASE WHEN a.status = 1 THEN 1 ELSE 0 END) as status_2,
COUNT(a.status) as total
FROM table1 rn LEFT JOIN
VIEW_AKTIFITAS a
ON a.id = rn.id AND
a.date >= DATE '2019-07-01' AND
a.date < DATE '2019-08-01'
GROUP BY rn.id, rn.name;
You do not need a subquery for the LEFT JOIN. The date comparisons are better done using direct date comparisons. Oracle allows you to use ISO-standard date formats with the DATE keyword.

this is my final code
SELECT t1.nip, t1.nama_pegawai,
count(CASE
WHEN t2.status = 1
and t2.tahun=2019
and t2.bulan=7
THEN
1
END) count_status_1,
count(CASE
WHEN t2.status = 2
and t2.tahun=2019
and t2.bulan=7
THEN
1
END) count_status_2,
count(CASE
WHEN t2.tahun=2019
and t2.bulan=7
THEN 1 END
) total_count
FROM VIEW_PEGAWAI t1
LEFT JOIN VIEW_AKTIFITAS t2
ON t2.nip = t1.nip
where t1.opd_id=14
and T1.JENIS_PEG<>3
group by t1.nip,t1.nama_pegawai

Related

How to get the next non-zero value in table partitioned by id?

Here is a subset of my table:
id
date
value
1
01/01/2022
5
1
02/02/2022
0
1
03/01/2022
0
1
04/02/2022
10
2
01/04/2022
5
2
02/04/2022
3
2
03/04/2022
0
2
04/04/2022
10
Where there are 0s in the value field, i would like to replace them with the non-zero value that occurs after the sequence of 0s are over, partitioned by id.
I have tried to use LAG but im really struggling as it takes the value above the current value in the table.
Any help will be appreciated.
Transformed table to look like
id
date
value
1
01/01/2022
5
1
02/02/2022
10
1
03/01/2022
10
1
04/02/2022
10
2
01/04/2022
5
2
02/04/2022
3
2
03/04/2022
10
2
04/04/2022
10
you can use cross apply;
select T1.id, T1.date, CASE WHEN T1.value = 0 THEN X.value ELSE T1.value END value from TestTable T1
OUTER APPLY (SELECT TOP 1 * FROM TestTable T2
WHERE T1.id = T2.id AND T2.date > T1.date
AND T2.value > 0
ORDER BY T2.date) X
sqlfiddle
Assuming by replace them you mean to update the table, simplest way would be a correlated subquery:
update t set value = (
select top(1) value
from t t2
where t2.id = t.id
and t2.value > 0
and t2.date > t.date
order by t2.date
)
where t.value = 0;
We group every 0 with the first value after it that's not 0 and then we use max() over() to replace the 0s in the group.
select id
,date
,max(value) over(partition by id, grp) as value
from
(
select *
,count(case when value != 0 then 1 end) over(partition by id order by date desc) as grp
from t
) t
order by id, date
id
date
value
1
2022-01-01
5
1
2022-02-02
10
1
2022-03-01
10
1
2022-04-02
10
2
2022-01-04
5
2
2022-02-04
3
2
2022-03-04
10
2
2022-04-04
10
Fiddle
You can do it using outer apply:
select
d.id, d.date_,
case when d.value != 0 then d.value else nz.value end as value
from data d
outer apply (
select min(value) as value
from data dd
where dd.id = d.id
and dd.date_ > d.date_
and dd.value <> 0
) nz
You can test on this db<>fiddle

How to group by period of time?

I have a table with messages and I need to find chats where were two or more messages in period of 10 seconds.
table
id message_id time
1 1 13:09:00
1 2 13:09:01
1 3 13:09:50
2 1 15:18:00
2 2 15:20:00
3 1 15:00:00
3 2 15:10:00
3 3 15:10:10
So the result looks like
id
1
3
I can't come up with the idea how to group by a period or maybe it can be done other way?
select id
from t
group by id, ?
having count(message_id) > 1
You can use a self-join:
with add_ymd(id, mid, dt) as (
select id, message_id, (date(now())||' '||time)::timestamp from messages
),
tm_counts as (
select t2.id, max(t2.n) + 1 tm from (
select t.id, t.mid, sum(case when extract(epoch from t.dt - t1.dt) <= 10 then 1 end) n
from add_ymd t join add_ymd t1 on t.dt > t1.dt group by t.id, t.mid) t2
where t2.n is not null group by t2.id
)
select id from tm_counts where tm > 1
Output:
id
---
1
3

sql server 2008 select statement for rows count

I have two table
Table_1:
id pa_name
1 A
2 B
3 C
Table_2:
id breakfast lunch dinner day day_des
1 Yes Yes No 1 des1
1 No No Yes 2 des2
1 Yes No Yes 3 des3
2 Yes Yes Yes 1 des11
2 Yes No No 2 des22
2 No Yes No 3 des33
I want the result like that
id pa_name breakfast lunch dinner day_des
1 A 2 1 2 des1
2 B 2 2 1 des11
I am using SQL Server 2008
This will give you the values in Comma seperated format
;WITH CTE AS
(
SELECT t1.id, t1.pa_name,
SUM(CASE WHEN breakfast = 'YES' then 1 end) as breakfast,
SUM(CASE WHEN lunch = 'YES' THEN 1 END) AS lunch,
SUM(CASE WHEN dinner= 'YES' THEN 1 END) AS dinner
FROM Table_1 AS t1
JOIN Table_2 AS t2 ON t1.id = t2.id
GROUP BY t1.id, t1.pa_name
)
SELECT *,
SUBSTRING(
(SELECT ', ' + day_des
FROM TABLE_2 T2
WHERE CTE.id=id
FOR XML PATH('')),2,200000) day_des
FROM CTE
SQL FIDDLE
Combine a JOIN with GROUP BY:
select t1.id, t1.pa_name,
SUM(case when breakfast = 'YES' then 1 end) as breakfast,
SUM(case when lunch = 'YES' then 1 end) as lunch,
SUM(case when dinner= 'YES' then 1 end) as dinner,
MIN(day_des)
from Table_1 as t1 join Table_2 as t2 on t1.id = t2.id
group by t1.id, t1.pa_name
But I'm a bit concerned about the recent edit, how is day_des supposed to be picked if there are different values? My choice was MIN value.

Combine 2 SQL queries

I'm stuck on a SQL issue, I'm almost sure it's all easy but I can't find a proper answer. Below here is the example. I want to combine those 2 queries :
SELECT
num_rata, SUM(val_imp*0.01) AS amount
FROM table1
WHERE
col1 <> 0
AND num_contract = 88
GROUP BY num_rata
That returns something like :
1 215.00
2 220.00
3 210.00
4 115.00
5 315.00
And :
SELECT
num_rata, SUM(val_ban*0.01) AS amount
FROM table2
WHERE
num_contract = 2988
GROUP BY num_rata;
Example result set :
1 15.00
2 615.00
3 275.00
4 285.00
5 285.00
6 275.00
7 260.00
8 215.00
9 215.00
As a final result I would like something like this :
1 215.00 15.00
2 220.00 615.00
3 210.00 275.00
4 115.00 285.00
5 315.00 285.00
6 275.00
7 260.00
8 215.00
9 215.00
Thank you for any hint.
fabien.
You could use either a union or an outer join - I would prefer a union, like this:
SELECT num_rata, sum(val_imp)*0.01 imp_amount, sum(val_ban)*0.01 ban_amount
from (SELECT num_rata, val_imp, 0 val_ban
FROM table1
WHERE col1 <> 0 AND num_contract = 88
UNION ALL
SELECT num_rata, 0 val_imp, val_ban
FROM table2
WHERE num_contract = 2988) v
GROUP BY num_rata;
I think this is what you want:
select
t1.num_rata,
SUM(t1.val_imp*0.01) AS amount1,
SUM(t2.val_ban*0.01) AS amount2
from
table1 t1 right outer join table2 t2 on t1.num_rata=t2.num_rata
where
t1.col1<>0 and
t1.num_contract = 88 and
t2.num_contract = 2988
GROUP BY t1.num_rata
Can you try what will this do:
SELECT num_rata, (
coalesce(
(SELECT SUM(val_imp*0.01) FROM table1 t1 WHERE t1.num_rata = foo.num_rata)
,0)
) as col1, (
coalesce(
(SELECT SUM(val_ban*0.01) FROM table2 t2 WHERE t2.num_rata = foo.num_rata)
,0)
) as col2 FROM
(
(SELECT num_rata FROM table1 WHERE col1 <> 0 AND num_contract = 88)
UNION ALL
(SELECT num_rata FROM table2 WHERE num_contract = 2988)
) as foo;
I might have syntax error (I am writing this from a university lecture), but this (or similar) should do the trick.

Design SQL Query for following case

Consider tables
Table1
id, name
1 xyz
2 abc
3 pqr
Table2
id title
1 Mg1
2 Mg2
3 SG1
Table3
Tb1_id tb2_id count
1 1 3
1 2 3
1 3 4
2 2 1
3 2 2
3 3 2
I want to do query to give result like
id title
1 MG1
2 MG2
3 Two or More Title
MG1 has higher preference if MG1 and count >= 1 then it is given as MG1 title , for others corresponding title is used and for count > 1 as two or more
I think this is what you are going for:
select t3.Tb1_id as id,
case
when mg1cnt.count >= 1 then 'MG1'
when cnt.count = 1 then upper(t2.title)
else 'Two or More Titles'
end as title
from (
select Tb1_id, count(*) as count
from Table3
group by Tb1_id
) cnt
inner join (
select Tb1_id, isnull(SUM(case when t2.title='mg1' then 1 end), 0) as count
from Table3 t3
inner join Table2 t2 on t3.tb2_id = t2.id
group by Tb1_id
) as mg1cnt on cnt.Tb1_id = mg1cnt.Tb1_id
inner join Table3 t3 on cnt.Tb1_id = t3.Tb1_id
inner join Table2 t2 on t3.tb2_id = t2.id
group by t3.Tb1_id,
case
when mg1cnt.count >= 1 then 'MG1'
when cnt.count = 1 then upper(t2.title)
else 'Two or More Titles'
end