Combine two selects - sql

I have a problem with two sql querys.
The one sql selects some ID's and the other one sets the id structur.
The problem is I can't get them both to work as one query.
The SQL which sets the structure:
select nstrid
from t_ousernstr
where kstrid = 116 And
Bis is null
Order by nstrid
The id's from that are like:
100
200
300
400
And the SQL which gets the user id's:
SELECT
T_OUSER.ID AS ID,
FROM
T_OUSER,
T_OUSERNSTR
WHERE
( T_OUSERNSTR.NSTRID = ANY(//here should be the id's from above))
(T_OUSERNSTR.VON is null or SYSDATE >=T_OUSERNSTR.VON) and
(T_OUSERNSTR.BIS is null) and
(T_OUSERNSTR.BEGINN IS NULL OR T_OUSERNSTR.BEGINN<= SYSDATE) and
(T_OUSERNSTR.ENDE is null or T_OUSERNSTR.ENDE> SYSDATE)
Order By T_OUSER.ID;

Use IN:
SELECT o.ID AS ID
FROM T_OUSER o
CROSS JOIN
T_OUSERNSTR n
WHERE n.NSTRID IN ( select nstrid
from t_ousernstr
where kstrid = 116
And Bis is null )
AND ( n.BIS IS NULL )
AND ( n.VON IS NULL OR SYSDATE >= n.VON )
AND ( n.BEGINN IS NULL OR SYSDATE >= n.BEGINN )
AND ( n.ENDE IS NULL OR SYSDATE < n.ENDE )
ORDER BY o.ID;
or EXISTS:
SELECT o.ID AS ID
FROM T_OUSER o
CROSS JOIN
T_OUSERNSTR n
WHERE EXISTS (
SELECT 1
FROM t_ousernstr x
WHERE x.kstrid = 116
AND x.Bis IS NULL
AND ( n.NSTRID = x.NSTRID OR ( n.NSTRID IS NULL AND x.NSTRID IS NULL ) )
)
AND ( n.BIS IS NULL )
AND ( n.VON IS NULL OR SYSDATE >= n.VON )
AND ( n.BEGINN IS NULL OR SYSDATE >= n.BEGINN )
AND ( n.ENDE IS NULL OR SYSDATE < n.ENDE )
ORDER BY o.ID;
or to remove the correlated sub-query you could use analytic functions:
SELECT o.ID AS ID
FROM T_OUSER o
CROSS JOIN
( SELECT n.*,
MAX( CASE WHEN kstrid = 116 THEN 1 ELSE 0 END )
OVER ( PARTITION BY bis, nstrid )
AS has_kstrid
FROM T_OUSERNSTR n
) n
WHERE n.has_kstrid = 1
AND ( n.BIS IS NULL )
AND ( n.VON IS NULL OR SYSDATE >= n.VON )
AND ( n.BEGINN IS NULL OR SYSDATE >= n.BEGINN )
AND ( n.ENDE IS NULL OR SYSDATE < n.ENDE )
ORDER BY o.ID;

Related

how to get data not exist in other table?

I am getting data based on maintenance_due_date before 7 days or sysdate greater then maintenance_due_date. Now I want to add one more condition that if data not available in maintenace_schedule table against instrument_no and plan_id then also I want to get rows against plan_id from activity_detail table.
SELECT
instrument_no,
frequency,
detail,
plan_id,
activity_id,
period,
-- maintenance_date,
maintenance_due_date
FROM
(
SELECT
ms.instrument_no,
ad.frequency,
ad.detail,
ad.plan_id,
ad.activity_id,
ad.period,
-- ms.maintenance_date,
MAX(ms.maintenance_due_date) maintenance_due_date
FROM
maintenance_schedule ms,
activity_detail ad
--left join maintenance_schedule ms1 on ms1.plan_id = ad.plan_id and ms1.instrument_no = ms.instrument_no --am.inactive <> 1
WHERE
ms.instrument_no = 10073
AND ad.plan_id = 100
AND ms.plan_id = ad.plan_id
and ad.end_date is null
AND ms.activity_id = ad.activity_id
GROUP BY
ms.instrument_no,
ad.frequency,
ad.detail,
ad.plan_id,
ad.activity_id,
ad.period
-- ms.maintenance_date
)
WHERE
( maintenance_due_date BETWEEN sysdate AND sysdate + 7
OR maintenance_due_date < sysdate)
order by activity_id asc, maintenance_due_date asc;
Your inner query should be changed to use LEFT JOIN so you could handle the null values from a missing row within both of WHERE clauses (inner and outer query). For readability I moved your inner query to CTE definitions and named it activities. You don't need to do that - you can leave it where it was if you wish so.
Here are your (imagined) sample data and the new activities CTE with some comments in the code:
WITH
maintenance_schedule AS
( Select 100 "PLAN_ID", 1 "ACTIVITY_ID", To_Date('20.12.2022', 'dd.mm.yyyy') "MAINTENANCE_DUE_DATE", 10073 "INSTRUMENT_NO", 'some other data' "SOME_COLUMN" From Dual Union All
Select 100 "PLAN_ID", 2 "ACTIVITY_ID", To_Date('29.12.2022', 'dd.mm.yyyy') "MAINTENANCE_DUE_DATE", 10073 "INSTRUMENT_NO", 'some other data' "SOME_COLUMN" From Dual
),
activity_detail AS
( Select 1 "ACTIVITY_ID", 100 "PLAN_ID", 'details about ...' "DETAIL", 'frequency X...' "FREQUENCY", 'A' "PERIOD", Null "END_DATE" From Dual Union All
Select 2 "ACTIVITY_ID", 100 "PLAN_ID", 'details about ...' "DETAIL", 'frequency Y...' "FREQUENCY", 'B' "PERIOD", Null "END_DATE" From Dual Union All
Select 3 "ACTIVITY_ID", 100 "PLAN_ID", 'details about ...' "DETAIL", 'frequency Z...' "FREQUENCY", 'C' "PERIOD", Null "END_DATE" From Dual
),
activities AS
( SELECT ms.instrument_no,
ad.frequency,
ad.detail,
ad.plan_id,
ad.activity_id,
ad.period,
MAX(ms.maintenance_due_date) maintenance_due_date
FROM activity_detail ad
-- LEFT JOIN (line below) is the solution to your problem because it gives you options to handle null values from a missing row
LEFT JOIN maintenance_schedule ms ON( ms.plan_id = ad.plan_id And ms.activity_id = ad.activity_id )
WHERE ( Nvl(ms.instrument_no, 10073) = 10073 AND ad.plan_id = 100 AND ad.end_date Is Null)
-- you can use this ---> ( Nvl(ms.instrument_no, 10073) = 10073 AND ad.plan_id = 100 AND ad.end_date Is Null)
-- OR this ---> ( ms.instrument_no = 10073 AND ad.plan_id = 100 AND ad.end_date Is Null) OR ( ms.instrument_no Is Null AND ad.plan_id = 100 AND ad.end_date Is Null)
GROUP BY ms.instrument_no,
ad.frequency,
ad.detail,
ad.plan_id,
ad.activity_id,
ad.period
)
with above data your main SQL shoulld be like below:
SELECT instrument_no,
frequency,
detail,
plan_id,
activity_id,
period,
maintenance_due_date
FROM activities -- moved your subquery to CTE definition above for readability - you can leave it here if you wish so
WHERE ( Nvl(maintenance_due_date, sysdate + 1) BETWEEN sysdate AND sysdate + 7 OR maintenance_due_date < sysdate)
-- you can use this ---> ( Nvl(maintenance_due_date, sysdate + 1) BETWEEN sysdate AND sysdate + 7 OR maintenance_due_date < sysdate)
-- OR this ---> ( maintenance_due_date BETWEEN sysdate AND sysdate + 7 OR maintenance_due_date < sysdate OR maintenance_due_date Is Null)
ORDER BY activity_id, maintenance_due_date
... R e s u l t :
INSTRUMENT_NO
FREQUENCY
DETAIL
PLAN_ID
ACTIVITY_ID
PERIOD
MAINTENANCE_DUE_DATE
10073
frequency X...
details about ...
100
1
A
20-DEC-22
10073
frequency Y...
details about ...
100
2
B
29-DEC-22
frequency Z...
details about ...
100
3
C
Additionaly, you can use, let's say, Nvl(INSTRUMENT_NO , 0) in the SELECT list to give a value for missing data if you want.

How to mimic an ON condition while using Union?

I have a query like this:
select
yyyy_mm_dd,
xml_id,
feature,
status
from
schema.t1
where
yyyy_mm_dd >= '2019-02-02'
union all
select
yyyy_mm_dd,
p_id as xml_id,
'payment' as feature,
case
when payment = 1 then 1
else 0
end as status
from
schema.t2
where
yyyy_mm_dd >= '2019-02-02'
Is there a way I can ensure no side of the union has a greater date than the other? With a join I could enforce this with an on condition on yyyy_mm_dd. I want to maintain the union but only until the max date which is available in both tables.
Is there a more efficient way to solve this than the solution I've come up with?
select
c.yyyy_mm_dd,
xml_id,
feature,
status
from
schema.t1 c
left join(
select
max(yyyy_mm_dd) as yyyy_mm_dd
from
schema.t2
where
yyyy_mm_dd >= '2020-10-01'
) m on m.yyyy_mm_dd = c.yyyy_mm_dd
where
c.yyyy_mm_dd >= '2020-10-01'
and m.yyyy_mm_dd is null
union all
select
c.yyyy_mm_dd,
p_id as xml_id,
'payment' as feature,
case
when payment = 1 then 1
else 0
end as status
from
schema.t2 c
left join(
select
max(yyyy_mm_dd) as yyyy_mm_dd
from
schema.t1
where
yyyy_mm_dd >= '2020-10-01'
) m on m.yyyy_mm_dd = c.yyyy_mm_dd
where
c.yyyy_mm_dd >= '2020-10-01'
and m.yyyy_mm_dd is not null
Create 2 CTEs for each of your queries and then select only the rows of each CTE that have matching yyyy_mm_dds in the other CTE:
with
cte1 as (
select yyyy_mm_dd, xml_id, feature, status
from schema.t1
where yyyy_mm_dd >= '2019-02-02'
),
cte2 as (
select yyyy_mm_dd, p_id as xml_id, 'payment' as feature,
case when payment = 1 then 1 else 0 end as status
from schema.t2
where yyyy_mm_dd >= '2019-02-02'
)
select c1.* from cte1 c1
where exists (select 1 from cte2 c2 where c2.yyyy_mm_dd = c1.yyyy_mm_dd)
union all
select c2.* from cte2 c2
where exists (select 1 from cte1 c1 where c1.yyyy_mm_dd = c2.yyyy_mm_dd)

Display two count from a single in two different column from a single table

I have a table where I record daily work of employees. I have a query where I display the current work for today for each employee and have another query where I display the total count of work for each employee.
I want to combine the 2 queries into a single one where I have a daily column and a cumulative column.
my query is below:
SELECT staff,
process_inprogress,
not_yet_completed
FROM (SELECT staff,
Count(number) AS Process_InProgress,
Count(team_name) AS Not_Yet_Completed
FROM dbo.empty_shell_workflow
WHERE ( end_date IS NULL )
AND ( process_name IS NOT NULL )
AND ( billing_amount IS NULL )
AND ( deletion IS NULL )
AND ( team_name = 'Team Vishma' )
AND ( CONVERT(DATE, start_date) = CONVERT(DATE, Getdate()) )
GROUP BY staff
UNION ALL
SELECT staff,
Count(number) AS Process_InProgress,
Count(team_name) AS Not_Yet_Completed
FROM dbo.empty_shell_workflow AS Empty_Shell_Workflow_1
WHERE ( team_name = 'Team Vishma' )
AND ( billing_amount IS NULL )
AND ( tag_number IS NULL )
AND ( initiator IS NOT NULL )
AND ( end_date IS NULL )
AND ( deletion IS NULL )
AND ( process_name IS NOT NULL )
GROUP BY staff) AS t
however it is being display only in a single column for both daily and cumulative
Below is how i want it to display
Staff Process_Progress(Daily) Not_YetCompleted(Cumulative)
A 2 5
B 0 1
C 6 8
however from the query above, the cumulative is being display in the daily column
Any idea, how can I modify the query?
you could try like below by using case when
with cte as
( SELECT staff,CONVERT(DATE, start_date) as date_of_month
Count(number) AS Process_InProgress
FROM dbo.empty_shell_workflow AS Empty_Shell_Workflow_1
WHERE ( team_name = 'Team Vishma' )
AND ( billing_amount IS NULL )
AND ( tag_number IS NULL )
AND ( initiator IS NOT NULL )
AND ( end_date IS NULL )
AND ( deletion IS NULL )
AND ( process_name IS NOT NULL )
GROUP BY staff,CONVERT(DATE, start_date)
) select staff, sum(case when date_of_month = CONVERT(DATE, Getdate()) then
Process_InProgress else 0 end) as Process_Progress_Daily,
sum(case when date_of_month != CONVERT(DATE, Getdate()) then
Process_InProgress else 0 end) as Not_YetCompleted
from cte
group by staff

Normalizing the data from denormalized table

I have data in my table like this
RepID|Role|Status|StartDate |EndDate |
-----|----|------|----------|----------|
10001|R1 |Active|01/01/2015|01/31/2015|
-----|----|------|----------|----------|
10001|R1 |Leavee|02/01/2015|02/12/2015|
-----|----|------|----------|----------|
10001|R1 |Active|02/13/2015|02/28/2015|
-----|----|------|----------|----------|
10001|R2 |Active|03/01/2015|03/18/2015|
-----|----|------|----------|----------|
10001|R2 |Leave |03/19/2015|04/10/2015|
-----|----|------|----------|----------|
10001|R2 |Active|04/11/2015|05/10/2015|
-----|----|------|----------|----------|
10001|R1 |Active|05/11/2015|06/13/2015|
-----|----|------|----------|----------|
10001|R1 |Leave |06/14/2015|12/31/9998|
-----|----|------|----------|----------|
I am looking for the output like this,
RepID|Role|StartDate |EndDate |
-----|----|----------|----------|
10001|R1 |01/01/2015|02/28/2015|
-----|----|----------|----------|
10001|R2 |03/01/2015|05/10/2015|
-----|----|----------|----------|
10001|R1 |05/11/2015|12/31/9998|
-----|----|----------|----------|
Whenever only the role change happens, I need to capture the start and EndDate. I tried different ways but couldn't get the output.
Any help is appreciated.
Below is the SQL i tried with but it doesnt help,
SELECT T1.RepID, T1.Role, Min(T1.StartDate) AS StartDate, Max(T1.EndDate) AS EndDate
FROM
(SELECT rD1.RepID, rD1.Role, rD1.StartDate, rD1.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
UNION
SELECT rD2.RepID, rD2.Role, rD2.StartDate, rD2.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
) T1
GROUP BY T1.RepID, T1.Role
UNION
SELECT EP.RepID, EP.Role AS DataValue, EP.StartDate, EP.EndDate
FROM repDetails EP
LEFT OUTER JOIN
(SELECT rD1.RepID, rD1.Role, rD1.StartDate, rD1.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
UNION
SELECT rD2.RepID, rD2.Role , rD2.StartDate, rD2.EndDate
FROM repDetails rD1
INNER JOIN repDetails rD2
ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate) AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL) OR (rD2.Role = '' AND rD1.Role = ''))
) T1
ON EP.RepID = T1.RepID AND EP.StartDate = T1.StartDate
WHERE T1.RepID IS NULL
The key here is to identify continuous rows until the role changes. This can be done by comparing the next row's role using the lead function and some additional logic to categorize all the previous rows into the same group.
After classifying them into groups, you just need to use min and max to get the start and end dates.
with groups as (
select x.*
,case when grp = 1 then 0 else 1 end + sum(grp) over(partition by repid order by startdate) grps
from (select t.*
,case when lead(role) over(partition by repid order by startdate) = role then 0 else 1 end grp
from t) x
)
select distinct repid,role
,min(startdate) over(partition by repid,grps) startdt
,max(enddate) over(partition by repid,grps) enddt
from groups
order by 1,3
Sample demo
do you just want the min(start) / max(end) dates for each repID and role?
If so, try:
Select
repID, role,
min(starDate),
max(endDate)
from
tbl
group by
repID, role
--
A more verbose solution, equivalent to VKP's:
SELECT
repid, ROLE, grpID,
MIN(startdate) AS min_startDateOverRole,
MAX(endDate) AS max_endDateOverRole
FROM
(SELECT
*, CASE WHEN isGrpEnd = 1 THEN 0 ELSE 1 end +
-- when on group end row, don't increment grpID.
-- Wait until start of next group
SUM(isGrpEnd) OVER(ORDER BY startdate) grpID
-- sum(all group end rows up to this one)
FROM
(SELECT
*,
CASE WHEN lead(ROLE) OVER(ORDER BY startdate) = ROLE
THEN 0 ELSE 1 end isGrpEnd
FROM t) x )
GROUP BY
repid, ROLE, grpid
ORDER BY
1,3

How can I count rows in this sql statement?

Could you help me by answering the following question?
How can I count rows in this sql statement?
SELECT `u`.*,
( 6371 * Acos(Cos(Radians(51.6992)) * Cos(Radians(localization_lat)) *
Cos(Radians(localization_lng) - Radians(
5.3042)) +
Sin
(
Radians(51.6992)) * Sin(Radians(localization_lat))
) ) AS
`distance`
FROM `ads` AS `u`
WHERE ( localization_zip_code LIKE '%5200%' )
AND ( date_end > '2016-03-19 19:34:43'
AND date_start < '2016-03-19 19:34:43' )
AND ( is_show = 1 )
AND ( is_accept_admin = 1 )
AND ( is_in_category_page = 1 )
HAVING ( `distance` < '70' )
ORDER BY `distance` ASC
select count(*) from table or select count(column name) from table method returns the number of records
try
SELECT Count(*),
`u`.*,
( 6371 * Acos(Cos(Radians(51.6992)) * Cos(Radians(localization_lat)) *
Cos(Radians(localization_lng) - Radians(
5.3042)) +
Sin
(
Radians(51.6992)) * Sin(Radians(localization_lat))
) ) AS
`distance`
FROM `ads` AS `u`
WHERE ( localization_zip_code LIKE '%5200%' )
AND ( date_end > '2016-03-19 19:34:43'
AND date_start < '2016-03-19 19:34:43' )
AND ( is_show = 1 )
AND ( is_accept_admin = 1 )
AND ( is_in_category_page = 1 )
HAVING ( `distance` < '70' )
ORDER BY `distance` ASC
Yes I know, but when I put count() in the statement I get 0 resulsts whilst without count() I get two in this case...
Can I use count() and HAVING in one sql statement?
You should be able to do it as a subquery SELECT COUNT(*) FROM (...) AS mysubquery. So maybe something like:
SELECT COUNT(*) FROM (
SELECT `u`.*,
( 6371 * Acos(Cos(Radians(51.6992)) * Cos(Radians(localization_lat)) *
Cos(Radians(localization_lng) - Radians(
5.3042)) +
Sin
(
Radians(51.6992)) * Sin(Radians(localization_lat))
) ) AS
`distance`
FROM `ads` AS `u`
WHERE ( localization_zip_code LIKE '%5200%' )
AND ( date_end > '2016-03-19 19:34:43'
AND date_start < '2016-03-19 19:34:43' )
AND ( is_show = 1 )
AND ( is_accept_admin = 1 )
AND ( is_in_category_page = 1 )
HAVING ( `distance` < '70' )
ORDER BY `distance` ASC
) AS mysubquery