can we have CASE expression/case result as a join table name in oracle - sql

I have 3 tables say Employee, Permanent_Emp and Contract_Emp
SELECT E.EMP_NO,
E.NAME,
JET.EMP_TYPE,
JET.DATE_JOINED
FROM Employee E
LEFT OUTER JOIN
/* Here Join Table Name(JET) it can be Permanent_Emp or Contract_Emp
which i want as a result of my case expression. */
ON (some condition here) ORDER BY E.EMP_NO DESC
case expression:
CASE
WHEN (E.EMP_TYPE_CODE >10 )
THEN
Permanent_Emp JET
ELSE
Contract_Emp JET
END
Note: table and column names are just for an example to understand requirement.
how can i have join table name from a case expression?

Something like this (although without a description of your tables, the exact join conditions or any sample data its hard to give a more precise answer):
SELECT E.EMP_NO,
E.NAME,
COALESCE( P.EMP_TYPE, C.EMP_TYPE ) AS EMP_TYPE
COALESCE( P.DATE_JOINED, C.DATE_JOINED ) AS DATE_JOINED
FROM Employee E
LEFT OUTER JOIN
Permanent_Emp P
ON ( E.EMP_TYPE_CODE > 10 AND E.EMP_NO = P.EMP_NO )
LEFT OUTER JOIN
Contract_Emp C
ON ( E.EMP_TYPE_CODE <= 10 AND E.EMP_NO = C.EMP_NO )
ORDER BY
E.EMP_NO DESC

use your case in select and join both tables
as
SELECT case when 1 then a.column
when 2 then b.column
end
from table c
join table a
on 1=1
join table2 b
on 1=1
but you cant use case while joining. its better to join both tables and in select use case statement with conditions as per your requirement

There is no way to conditionally add tables to a query in static SQL. If the relevant columns in Permanent_Emp and Contract_Emp are roughly equivalent, you could use a union in a sub-query.
SELECT *
FROM employee e
JOIN
(SELECT employee_id, relevant_column, 'P' AS source_indicator
FROM permanent_emp
UNION ALL
SELECT employee_id, relevant_column, 'C' AS source_indicator
FROM contract_emp) se
ON e.employee_id = se.employee_id
AND ( (e.emp_type_code > 10 AND source_indicator = 'P')
OR (e.emp_type_code <= 10 AND source_indicator = 'C'))

Using Alan's query as a starting point you can still use a case statement, just move it to the join condition:
SELECT *
FROM employee e
JOIN (
SELECT employee_id
, relevant_column
, 'P' AS source_indicator
FROM permanent_emp
UNION ALL
SELECT employee_id
, relevant_column
, 'C' AS source_indicator
FROM contract_emp
) se
ON se.employee_id = e.employee_id
and se.source_indicator = case when e.emp_type_code > 10
then 'P'
else 'C'
end
The only difference between this query and Allan's is the use of a case statement instead of an or statement.

Related

How to output a new column in a SELECT query based on a condition?

Currently, I have this SQL query:
SELECT AVG(ttbe.MarkGiven) FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion), ctbe.EmployeeId
At the moment, this returns the average mark of a single employee on a course which was completed on a certain year, calculated across each of the tests it contains (a course can have multiple tests).
I want to output an additional column to the SELECT query which specifies whether that employee has passed the course, based on a threshold. For example, if AVG(ttbe.MarkGiven) >= 40 then it would return 'Y' in the new column, otherwise it would return 'N'. What's the simplest and most efficient way of achieving this?
you could use the CASE expression like that:
SELECT AVG(ttbe.MarkGiven),
CASE WHEN AVG(ttbe.MarkGiven) >= 40 THEN 'Y' ELSE 'N' END as exam_passed
FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion),
ctbe.EmployeeId
https://www.oracletutorial.com/oracle-basics/oracle-case/#:~:text=Oracle%20CASE%20expression%20allows%20you,that%20accepts%20a%20valid%20expression.
You could use your current query as a CTE and then from the CTE use the reulst and compare it to your threshold
The advantag of the CTE is that you will not have to recalculate your AVG(ttbe.MarkGiven)
WITH _CTE as
(
SELECT AVG(ttbe.MarkGiven) as Col1
FROM tblTestsTakenByEmployee ttbe
INNER JOIN tblCoursesTakenByEmployee ctbe
ON ttbe.EmployeeId = ctbe.EmployeeId
LEFT JOIN tblCourse c
ON ctbe.CourseId = c.CourseId
WHERE ctbe.HasCompletedCourse = 'Y'
GROUP BY ctbe.CourseId, c.CourseName, EXTRACT(YEAR FROM ctbe.DateOfCourseCompletion), ctbe.EmployeeId
)
Select Col1
,CASE
WHEN Col1 >= 50 THEN 'Y'
ELSE 'N'
END AS Col1_Threshold

select query with where in the sub query

i have query select for report like this,
select * from (
select *
FROM
CTIORASQL.time_sheet ts
LEFT JOIN(
SELECT
emp.employee_id,
SUM(
CASE
WHEN emp.main_company_id = 9
THEN
CASE
WHEN sce.schedule_type_id=1 THEN sce.total_minute
END
WHEN sce.activity_id = 21 THEN sce.total_minute
END)/60 utl_tot
FROM CTIORASQL.time_sheet ts
LEFT JOIN CTIORASQL.time_sheet_schedule sce ON ts.time_sheet_id = sce.time_sheet_id
LEFT JOIN CTIORASQL.m_employee emp ON ts.requester_id = emp.employee_id
where ts.schedule_date BETWEEN '02-02-2020' and '07-10-2020'
GROUP BY emp.employee_id
) utl ON utl.employee_id = emp.employee_id
)
where ts.schedule_date BETWEEN '02-02-2020' and '07-10-2020'
i have same where in the sub query, how to make where condition to just one condition? thankyou
in the condition i cannot put the sql code to back end, ijust use sqldeveloper tools
Is there any reason for the outer query. It seems same as the subquery. Just use subquery for your desired result:
select *
FROM
CTIORASQL.time_sheet ts
LEFT JOIN(
SELECT
emp.employee_id,
SUM(
CASE
WHEN emp.main_company_id = 9
THEN
CASE
WHEN sce.schedule_type_id=1 THEN sce.total_minute
END
WHEN sce.activity_id = 21 THEN sce.total_minute
END)/60 utl_tot
FROM CTIORASQL.time_sheet ts
LEFT JOIN CTIORASQL.time_sheet_schedule sce ON ts.time_sheet_id = sce.time_sheet_id
LEFT JOIN CTIORASQL.m_employee emp ON ts.requester_id = emp.employee_id
where ts.schedule_date BETWEEN '02-02-2020' and '07-10-2020'
GROUP BY emp.employee_id
) utl ON utl.employee_id = emp.employee_id
If you have any other purpose for the outer query please explain.

how to join a table on a subquery that uses order by and limit

For each row from table tClass matching a given where clause,
join on the first row in tEv, sorted by time, where tEv.class_id = tClass.class_id
The following code throws the error
ORA-01799: a column may not be outer-joined to a subquery
select
c.class_id,
c.class_name,
e.start_time,
e.ev_id
from
tClass c
left join tEv e on (
e.ev_id = (
select
ss1.ev_id
from (
select
ed.ev_id
from
tEvDisp ed,
tEv e
where
ed.class_id = c.class_id
and ed.viewable = 'Y'
and ed.display_until > localtimestamp
and e.ev_id = ed.ev_id
order by
e.start_time
) ss1
where
rownum = 1
)
)
where
c.is_matching = 'Y';
How can this be rewritten to do what is described?
The above is for oracle, but needs to work in sqlite (substituting where necessary)
No idea about SQLite - that would need to be a separate question if this doesn't work - but for Oracle you could do something like this:
select c.class_id,
c.class_name,
e.start_time,
e.ev_id
from tClass c
left join (
select class_id, ev_id, start_time
from (
select ed.class_id,
ed.ev_id,
e.start_time,
row_number() over (partition by ed.class_id order by e.start_time) as rn
from tEvDisp ed
join tEv e on e.ev_id = ed.ev_id
where ed.viewable = 'Y'
and ed.display_until > localtimestamp
)
where rn = 1
) e on e.class_id = c.class_id
where c.is_matching = 'Y';
This uses a subquery which finds the most tEv data, using an analytic row_number() to identify the latest data for each class_id, which is restricted by the rn = 1 filter.
That subquery, consisting of at most one row per class_id, is then used the left outer join against tClass.
This sort of construct should get you what you need. You can fix the details.
select c.classid
, c.classname
, temp.maxstarttime
from tClass c left join (
select c.classid id
max(e.start_time) maxstarttime
from tClass join tEv on tEv.classId = tClass.ClassId
where whatever
group by c.classid) temp on c.classid = temp.id

how to get the count in SQL Server?

I have tried a lot to figure how to get the count from two tables with respect to master table
I have three tables
Using these table values I need to get this output..
Tried but could get the desired result
http://en.wikipedia.org/wiki/Join_(SQL)
SQL - LEFT OUTER JOIN and WHERE clause
http://forums.devshed.com/oracle-development-96/combination-of-left-outer-join-and-where-clause-383248.html
You have to first GROUP BY in subqueries, then JOIN to the main table:
SELECT
a.AttributeId
, COALECSE(cntE, 0) AS cntE
, COALECSE(cntM, 0) AS cntM
FROM
AttributeMaster AS a
LEFT JOIN
( SELECT
AttributeId
, COUNT(*) AS cntE
FROM
EmployeeMaster
GROUP BY
AttributeId
) em
ON em.AttributeId = a.AttributeId
LEFT JOIN
( SELECT
AttributeId
, COUNT(*) AS cntM
FROM
MonthlyDerivedMaster
GROUP BY
AttributeId
) mdm
ON mdm.AttributeId = a.AttributeId
SELECT AttributeId,
(SELECT COUNT(Eid) FROM EmployeeMaster WHERE AttributeMaster.AttributeId = EmployeeMaster.AttributeId) as master_eid,
(SELECT COUNT(Eid) FROM MonthnlyDerivedMaster WHERE AttributeMaster.AttributeId = MonthnlyDerivedMaster.AttributeId) as monthly_eid
FROM AttributeMaster

SQL Server query : SELECT 1 WHERE EXISTS versus SELECT TOP 1 1

I need to present a flag - 0 if a condition is not meet, 1 if it is - and I can do it in two different ways :
Get Employee ID, name, 1 if has others in suborder - 0 if not :
SELECT e.ID
, e.Name
, ISNULL ( ( SELECT TOP 1 1 FROM Employee se WHERE se.ManagerID = e.ID ) , 0 ) AS HasSubordinates
FROM Employee e
or
SELECT e.ID
, e.Name
, ISNULL ( ( SELECT 1 WHERE EXISTS ( SELECT * FROM Employee se WHERE se.ManagerID = e.ID ) ) , 0 ) AS HasSubordinates
FROM Employee e
Which version would you choose and why ?
Update 1
How about this one ?
SELECT o.ID
, o.Name
, o.StartDate
, o.EndDate
, ISNULL ( ( SELECT TOP 1 1 FROM changes c WHERE c.ChangeDate BETWEEN o.StartDate AND o.EndDate ) , 0 ) AS IsChanged
FROM Orders o
or
SELECT o.ID
, o.Name
, o.StartDate
, o.EndDate
, ISNULL ( ( SELECT 1 WHERE EXISTS ( SELECT * FROM changes c WHERE c.ChangeDate BETWEEN o.StartDate AND o.EndDate ) ), 0 ) AS IsChanged
FROM Orders o
Neither, I'd use:
SELECT t.id,
t.name,
CASE WHEN x.managerid IS NULL THEN 0 ELSE 1 END AS HasSubordinates
FROM EMPLOYEE t
LEFT JOIN (SELECT DISTINCT
e.managerid
FROM EMPLOYEE e) x ON x.managerid = t.id
...because correlated SELECTS in the SELECT clause are bad - they do not scale, because they execute for every row returned. Which means the more rows you have, the more times the correlated SELECT will be called.
I wouldn't do either...
The reason being that (and this is as far as I know) when you have a subselect in the select statement, that subselect will be executed once for every row that is returned. Hence if you had 100 rows returned by the main query you would in effect be running 101 queries. When you use a join you are only doing one query that needs to match the left and the right together. Note it would help if you have an index on ManagerId.
Try something like this:
SELECT e.ID,
e.Name,
COUNT(se.ID) AS TotalStaff
FROM Employee e
LEFT JOIN Employee se
ON se.ManagerID = e.ID
GROUP BY e.ID, e.Name
This is slightly different to what you had as I am returning the total and not just a 0|1 but that is easy to change if you need the 1|0...
Let me know if this helps