Full outer self join or something else - sql

MID RID Name IsVisible
100 1 AA 1
100 2 AA 0
101 1 BB 1
101 2 BB 1
102 1 CC 0
102 2 CC 0
103 1 DD 0
103 2 DD 1
How can i select distinct MID where IsVisible=0 in all RID.
Expected result is 102 which is IsVisible=0 for all RID.

The question is a bit unclear, but perhaps this is what you want...?
Do a GROUP BY, check that both max and min IsVisible = 0.
select MID
from tablename
group by MID
having max(cast(IsVisible as int)) = 0

Use NOT EXISTS
select distinct t1.mid
from your_table t1
where not exists (
select 1
from your_table t2
where t1.mid = t2.mid and t2.isVisible != 0
)

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

Conditional sum in SQL (SAS) (SUMIFS equivalent)

Let say I am working under SAS EG and I have 2 tables:
Table1:
Id Item
1 A
1 B
1 C
2 B
2 D
3 C
3 E
3 F
Table2:
Id Item Amount
1 A 99
2 C 100
1 B 100
2 A 90
1 A 93
3 B 92
1 E 93
2 B 99
1 A 93
Now I would like to take the sum conditional for my table1 from table2 (when the ID and the Item match).
Id Item Want
1 A 285
1 B 100
1 C 0
2 B 99
2 D 0
3 C 0
3 E 0
3 F 0
So what am I supposed to do in SQL?
Thanks in advance.
Use a correlated subquery to SUM:
select t1.Id, t1.Item,
(select sum(t2.amount) from table2 t2 where t2.id = t1.id and t2.item = t1.item)
from table1 t1
This looks like aggregation to me, with a left join:
select t1.id, t1.item,
coalesce(sum(t2.amount), 0) as want
from table1 t1 left join
table2 t2
on t1.id = t2.id and t2.item = t2.item
group by t1.id, t1.item;

How to join and count with multiple condition in oracle

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

If value in both table then assign 1

Table1 CompanyID Location #-of-employees
5234 NY 10
5268 DC 2
5879 NY 8
6897 KS 100
8789 CA 1
9992 OH 201
9877 TX 15
Table2 CompanyID #-of-Shareholders
5234 5
5879 2
6897 4
8789 2
I have two table with the column CompanyID. In table2 you can find companies that have shareholders and in table1 you can find all the companies. So in table 1 I want to add a dummy variable that assign a 1 if the companyID is in table2(which means the company has shareholders) and a 0 if not.
Expected output:
Table1 CompanyID Location #-of-employees Dummy
5234 NY 10 1
5268 DC 2 0
5879 NY 8 1
6897 KS 100 1
8789 CA 1 1
9992 OH 201 0
9877 TX 15 0
I tried using this query but it doesn't give me the output I expect.
SELECT CASE WHEN companyID IN table2 THEN 1
ELSE 0
END AS dummy
FROM table1
You have to use the Subquery for this. the below code working fine.
SELECT CASE WHEN companyID in(select CompanyId from table2) THEN 1
ELSE 0
END AS dummy
FROM table1
You can use EXISTS
SELECT CASE
WHEN EXISTS(SELECT 1 FROM Table2 AS T2 WHERE T1.CompanyID = T2.CompanyID) THEN 1
ELSE 0
END AS Dummy
FROM Table1 AS T1;
If your DB's version is 2012+ then, use with left join as :
select t1.*, iif(#_of_Shareholders is null, 0, 1) as dummy
from table1 t1
left join table2 t2
on ( t1.CompanyID = t2.CompanyID );
else
select t1.*,
( case when #_of_Shareholders is null then 0 else 1 end )
as dummy
from table1 t1
left join table2 t2
on ( t1.CompanyID = t2.CompanyID );
or
select t1.*,
sign(coalesce(#_of_Shareholders,0))
as dummy
from table1 t1
left join table2 t2
on ( t1.CompanyID = t2.CompanyID );
Rextester Demo

Return only unique values

I'm trying to get counts of how many jobs were done, regardless of personnel working on them. What I need to do is filter out any duplicates, the catch is that the entire row isn't duplicate so DISTINCT won't work here. I want to filter out if there is any duplication based on JobCode, JobType TaskTime and day of week. So my table looks like the following:
JobCode JobType TaskTime EmployeeID M Tu W Th F Sa Su
==================================================================
1800 1 06:49 101 1 1 1 1 1 0 0
1800 1 06:49 102 1 0 0 0 0 0 0
1800 1 07:04 101 1 1 1 1 1 0 0
1800 1 07:26 101 1 1 1 1 1 0 0
1800 1 07:49 101 1 1 1 1 1 0 0
1800 2 15:55 101 1 1 1 1 1 0 0
1800 1 16:20 101 1 1 1 1 1 0 0
1800 1 16:50 101 1 1 1 1 1 0 0
1800 2 16:55 101 1 1 1 1 1 0 0
My SQL Query is like this
SELECT t1.JobCode, t1.JobType,
t1.M, t1.Tu, t1.W, t1.Th, t1.F, t1.Sa, t1.Su,
SUM(t1.M + t1.Tu + t1.W + t1.Th + t1.F + t1.Sa + t1.Su) as Totals
FROM Table1 AS t1
JOIN Table1 AS t1_overlap ON
t1_overlap.EmployeeID = t1.EmployeeID AND
t1_overlap.JobType = t1.JobType AND
t1_overlap.TaskTime = t1.TaskTime
AND
(
(t1.M = 1 AND t1_overlap.M = t1.M) OR
(t1.Tu = 1 AND t1_overlap.Tu = t1.Tu) OR
(t1.W = 1 AND t1_overlap.W = t1.W) OR
(t1.Th = 1 AND t1_overlap.Th = t1.Th) OR
(t1.F = 1 AND t1_overlap.F = t1.F) OR
(t1.Sa = 1 AND t1_overlap.Sa = t1.Sa) OR
(t1.Su = 1 AND t1_overlap.Su = t1.Su)
)
GROUP BY t1.JobCode, t1.JobType, t1.M, t1.Tu, t1.W, t1.Th, t1.F, t1.Sa, t1.Su
The data returned is like this
JobCode JobType M Tu W Th F Sa Su Totals
==================================================
1800 1 1 0 0 0 0 0 0 1
1800 1 1 1 1 1 1 0 0 30
1800 2 1 1 1 1 1 1 1 10
What I want to see is only unique values, so I don't want that first line that shows the job was worked on by employee 102 on only M because I'm already seeing that employee 101 worked on that same job on that same day and time. So what I want to see instead is the following:
JobCode JobType M Tu W Th F Sa Su Totals
==================================================
1800 1 1 1 1 1 1 0 0 30
1800 2 1 1 1 1 1 0 0 10
Really I don't need to see the days of the week, I'm just showing them here so I can see whats being returned. All I actually need to see for output is the JobCode, JobType and Totals like the following:
JobCode JobType Totals
======================
1800 1 30
1800 2 10
Help is greatly appreciated.
I think a quick subquery where you grab the max of each day, grouping by your key, then sum the results, would do the trick:
SELECT
jobcode,
jobtype,
sum(monday+tuesday+wednesday+thursday+friday+saturday+sunday) AS total
FROM
(
SELECT
jobcode,
jobtype,
tasktime,
max(m) as monday,
max(tu) as tuesday,
max(w) as wednesday,
max(th) as thursday,
max(f) as friday,
max(sa) as saturday,
max(su) as sunday
FROM Table1 T1
GROUP BY jobcode, jobtype, tasktime
) t2
GROUP BY jobcode, jobtype
There may be something more eloquent than that, but this should get the job done.
Try with the below query.
;With cte1
as
(SELECT ROW_NUMBER()OVER(PArtition by t1.JobCode,t1.JobType order by t1.JobCode,t1.JobType) RNO,t1.JobCode, t1.JobType,
SUM(t1.M + t1.Tu + t1.W + t1.Th + t1.F + t1.Sa + t1.Su) OVER(partition by t1.JobCode,t1.JobType ORDER BY t1.JobCode,t1.JobType ) as Totals
FROM Table1 AS t1
JOIN Table1 AS t1_overlap ON
t1_overlap.EmployeeID = t1.EmployeeID AND
t1_overlap.JobType = t1.JobType AND
t1_overlap.TaskTime = t1.TaskTime
AND
(
(t1.M = 1 AND t1_overlap.M = t1.M) OR
(t1.Tu = 1 AND t1_overlap.Tu = t1.Tu) OR
(t1.W = 1 AND t1_overlap.W = t1.W) OR
(t1.Th = 1 AND t1_overlap.Th = t1.Th) OR
(t1.F = 1 AND t1_overlap.F = t1.F) OR
(t1.Sa = 1 AND t1_overlap.Sa = t1.Sa) OR
(t1.Su = 1 AND t1_overlap.Su = t1.Su)
))
SELECT t1.JobCode, t1.JobType,Totals
FROM cte1
WHERE RNO=1
Basically, you can use the super-awesome ROW_NUMBER function and wrap the query so that you effectively 1) define a grouping, and 2) take ONLY the first row of each group. Look closely at the over (partition by ... order by ...) clause to understand how you can control the grouping and the "ranking" (which ones you want to make it through to the results).
select JobCode, JobType, Totals
from (
SELECT t1.JobCode, t1.JobType, SUM(t1.M + t1.Tu + t1.W + t1.Th + t1.F + t1.Sa + t1.Su) as Totals
,row_number() over (partition by t1.JobCode order by t1.JobType) as rseq
FROM Table1 AS t1
JOIN Table1 AS t1_overlap ON
t1_overlap.EmployeeID = t1.EmployeeID AND
t1_overlap.JobType = t1.JobType AND
t1_overlap.TaskTime = t1.TaskTime
AND
(
(t1.M = 1 AND t1_overlap.M = t1.M) OR
(t1.Tu = 1 AND t1_overlap.Tu = t1.Tu) OR
(t1.W = 1 AND t1_overlap.W = t1.W) OR
(t1.Th = 1 AND t1_overlap.Th = t1.Th) OR
(t1.F = 1 AND t1_overlap.F = t1.F) OR
(t1.Sa = 1 AND t1_overlap.Sa = t1.Sa) OR
(t1.Su = 1 AND t1_overlap.Su = t1.Su)
)
GROUP BY t1.JobCode, t1.JobType, t1.M, t1.Tu, t1.W, t1.Th, t1.F, t1.Sa, t1.Su
) x
where rseq = 1 --filter to keep only the "first" row (JobType) for each JobCode
Note that the comments about "how do you decide which one to keep" are valid, and this example assumes you want to see "one row per JobCode", and keep the "first JobType for that JobCode". This can be adjusted to fit, if you'll explain the logic you want to use a little more clearly.