SQL subquery filter using parent table fields - sql

I have a query as below:
SELECT tr.AccountFK ,
TransactionRequestStatus = CASE
WHEN tr.TransactionRequestStatusFK = 2 THEN 'Accepted'
ELSE 'Rejected'
END,
tr.TransactionRequestID
FROM [CRM].[acc].[TransactionRequest] as tr
INNER JOIN CRM.acc.TransactionRequestHistory as trh
ON tr.TransactionRequestID = trh.TransactionRequestFK
where tr.TransactionRequestTypeFK = 3 --bill split
and TransactionRequestStatusFK in (2, 3) --Approved, Rejected
--Filter- on each item
and (tr.TransactionRequestStatusFK = 2 and tr.TransactionRequestID not in (select tr.TransactionRequestID from crm.acc.TransactionRequest tr
INNER JOIN CRM.acc.TransactionRequestHistory as trh
ON tr.TransactionRequestID = trh.TransactionRequestFK
where tr.TransactionRequestID = trh.TransactionRequestFK
and trh.EventFK = 15)
or
tr.TransactionRequestStatusFK = 3 and tr.Notes not like '%No Action%')
group by tr.AccountFK, tr.TransactionRequestStatusFK, tr.TransactionRequestID
I have a subquery in the where clause to apply a filter on each item but this subquery join is the same has the join in the main query, is there any way to re-use the main query table and avoid repeating same code in the subquery.

Using CTE. Assuming the column names in both crm.acc.TransactionRequest and CRM.acc.TransactionRequestHistory are distinct, the following should work. If they aren't, then you'll have to specify the columns with their alias in the Select of cte.
with cte as
(select * from crm.acc.TransactionRequest tr
INNER JOIN CRM.acc.TransactionRequestHistory as trh
ON tr.TransactionRequestID = trh.TransactionRequestFK)
SELECT AccountFK ,
TransactionRequestStatus = CASE
WHEN TransactionRequestStatusFK = 2 THEN 'Accepted'
ELSE 'Rejected'
END,
TransactionRequestID
FROM cte
where TransactionRequestTypeFK = 3 --bill split
and TransactionRequestStatusFK in (2, 3) --Approved, Rejected
--Filter- on each item
and (TransactionRequestStatusFK = 2 and TransactionRequestID not in
(select TransactionRequestID from cte
where TransactionRequestID = TransactionRequestFK
and EventFK = 15)
or
TransactionRequestStatusFK = 3 and Notes not like '%No Action%')
group by AccountFK, TransactionRequestStatusFK, TransactionRequestID

Related

Conditionally adding a column in a SQL query

I'm trying to add a conditional SELECT column in a query and I'm having trouble writing it out:
SELECT v.GLTypeID,
v.something,
v.somethignElse,
CASE WHEN (SELECT TOP 1 Value FROM InterfaceParam WHERE InterfaceId = 0 AND Descr = 'gf') = 1 THEN a.CreditID ELSE NULL END AS CreditMemoID
FROM vGLDetails2 v
....
LEFT OUTER JOIN AssociationFund f
ON v.FundID = f.FundID
LEFT JOIN dbo.APLedger a ON v.RelID = a.APLedgerID AND v.RelTypeID IN (39, 40)
....
ORDER BY v.Code;
The query above works, however if the CASE statement is still returning an additional column regardless of the result of the subquery. How can I remove it if the subquery doesn't return a row?
How can I do this?
Change the location of AS. For example:
SELECT v.GLTypeID,
v.something,
v.somethignElse,
CASE WHEN (
SELECT TOP 1 Value
FROM InterfaceParam
WHERE InterfaceId = 0 AND Descr = 'creditMemo') = 1
THEN a.CreditID -- AS is not valid here
END AS CreditMemoID -- AS is valid here
FROM vGLDetails2 v
....
LEFT OUTER JOIN AssociationFund f
ON v.FundID = f.FundID
LEFT JOIN dbo.APLedger a ON v.RelID = a.APLedgerID AND v.RelTypeID IN (39, 40)
....
ORDER BY v.Code;
Note: I removed ELSE NULL since this is the default behavior of CASE.

How to get fields from multiple tables

I want to get fields from 2 different tables . The last field candidate_score_id has a many to one relationship. So how should I join the below 2 queries
1) To get candidate_score_id from the candidate_score table.
select candidate_score_id from candidate_score a where
a.assessment_id = NEW.assessment_id and
a.candidate_id = NEW.candidate_id and
a.attempt_Count = NEW.attempt_count;
2) To insert different fields in to the candidate_score_details table. The field in this table should be obtained by query above.
insert into candidate_score_details(candidate_score_details_id, candidate_id, assessment_id, attempt_count, score_type, score_tag,correct, candidate_score_id)
select uuid();
select a.candidate_id, a.assessment_id,a.attempt_count,"BY-COMPLEXITY",
case c.complexity
when 1 then "HIGH"
when 2 then "MEDIUM"
when 3 then "LOW"
end, count(*) from candidate_answer a, answer_key b, question_meta_data c where a.candidate_id = NEW.candidate_id and
a.assessment_id = NEW.assessment_id and
a.attempt_count = NEW.attempt_count and
a.assessment_id = b.assessment_id and
a.question_id = b.question_number and
a.response = b.answer and
a.question_id = c.question_number
group by a.candidate_id, a.assessment_id, a.attempt_count, c.complexity;
Just looking at the SQL joining aspect of your question, you'll need to specify the table I THINK you're aliasing a 2nd table with the "NEW" reference. If that's the case, then the query would be (replacing "OTHER_TABLE_NAME" with the name of the 2nd table:
select a.candidate_score_id
from candidate_score a
left join OTHER_TABLE_NAME new on
and a.assessment_id = NEW.assessment_id
and a.candidate_id = NEW.candidate_id
and a.attempt_Count = NEW.attempt_count
Seems that Query 1 has the same 3 criteria on the "candidate_score" table as for the "candidate_answer" table in Query 2.
So how about adding a LEFT JOIN of "candidate_score" to "candidate_answer" on those 3 fields?
For example:
INSERT INTO candidate_score_details
(
candidate_score_details_id,
candidate_id,
assessment_id,
attempt_count,
score_type,
score_tag,
correct,
candidate_score_id
)
SELECT
uuid(),
answer.candidate_id,
answer.assessment_id,
answer.attempt_count,
'BY-COMPLEXITY' AS score_type,
(CASE meta.complexity
WHEN 1 THEN 'HIGH'
WHEN 2 THEN 'MEDIUM'
WHEN 3 THEN 'LOW'
END) AS score_tag,
COUNT(*) AS correct,
MAX(score.candidate_score_id) AS max_candidate_score_id
FROM candidate_answer AS answer
JOIN answer_key AS akey
ON (akey.assessment_id = answer.assessment_id AND akey.question_number = answer.question_id AND akey.answer = answer.response)
LEFT JOIN candidate_score AS score
ON (score.candidate_id = answer.candidate_id AND score.assessment_id = answer.assessment_id AND score.attempt_count = answer.attempt_count)
LEFT JOIN question_meta_data AS meta
ON meta.question_number = answer.question_id
WHERE answer.candidate_id = NEW.candidate_id
AND answer.assessment_id = NEW.assessment_id
AND answer.attempt_count = NEW.attempt_count
GROUP BY answer.candidate_id, answer.assessment_id, answer.attempt_count, meta.complexity;

How to use alias of a subquery to get the running total?

I have a UNION of 3 tables for calculating some balance and I need to get the running SUM of that balance but I can't use PARTITION OVER, because I must do it with a sql query that can work in Access.
My problem is that I cannot use JOIN on an alias subquery, it won't work.
How can I use alias in a JOIN to get the running total?
Or any other way to get the SUM that is not with PARTITION OVER, because it does not exist in Access.
This is my code so far:
SELECT korisnik_id, imePrezime, datum, Dug, Pot, (Dug - Pot) AS Balance
FROM (
SELECT korisnik_id, k.imePrezime, r.datum, SUM(IIF(u.jedinstven = 1, r.cena, k.kvadratura * r.cena)) AS Dug, '0' AS Pot
FROM Racun r
INNER JOIN Usluge u ON r.usluga_id = u.ID
INNER JOIN Korisnik k ON r.korisnik_id = k.ID
WHERE korisnik_id = 1
AND r.zgrada_id = 1
AND r.mesec = 1
AND r.godina = 2017
GROUP BY korisnik_id, k.imePrezime, r.datum
UNION ALL
SELECT korisnik_id, k.imePrezime, rp.datum, SUM(IIF(u.jedinstven = 1, rp.cena, k.kvadratura * rp.cena)) AS Dug, '0' AS Pot
FROM RacunP rp
INNER JOIN Usluge u ON rp.usluga_id = u.ID
INNER JOIN Korisnik k ON rp.korisnik_id = k.ID
WHERE korisnik_id = 1
AND rp.zgrada_id = 1
AND rp.mesec = 1
AND rp.godina = 2017
GROUP BY korisnik_id, k.imePrezime, rp.datum
UNION ALL
SELECT uu.korisnik_id, k.imePrezime, uu.datum, '0' AS Dug, SUM(uu.iznos) AS Pot
FROM UnosUplata uu
INNER JOIN Korisnik k ON uu.korisnik_id = k.ID
WHERE korisnik_id = 1
GROUP BY uu.korisnik_id, k.imePrezime, uu.datum
) AS a
ORDER BY korisnik_id
You can save a query (let's name it Query1) for the UNION of the 3 tables and then create another query that returns each row in the first query and calculates the sum of the rows that are before it (optionally checking that they are in the same group).
It should be something like this:
SELECT *, (
SELECT SUM(Value) FROM Query1 AS b
WHERE b.GroupNumber=a.GroupNumber
AND b.Position<=a.Position
) AS RunningSum
FROM Query1 AS a
However, it's more efficient to do that in the report.

Return a Count of 0 When No Rows

OK, I've looked this up and tried a number of solutions, but can't get it to work. I'm a bit of a novice. Here's my original query - how can I get it to return 0 for an account when there are no results in the student table?
SELECT a.NAME
,count(s.student_sid)
FROM account a
JOIN inst i ON a.inst_sid = i.root_inst_sid
JOIN inst_year iy ON i.inst_sid = iy.inst_sid
JOIN student s ON iy.inst_year_sid = s.inst_year_sid
WHERE s.demo = 0
AND s.STATE = 1
AND i.STATE = 1
AND iy.year_sid = 16
AND a.account_sid IN (
20187987
,20188576
,20188755
,52317128
,20189249
)
GROUP BY a.NAME;
Use an outer join, moving the condition on that table into the join:
select a.name, count(s.student_sid)
from account a
join inst i on a.inst_sid = i.root_inst_sid
join inst_year iy on i.inst_sid = iy.inst_sid
left join student s on iy.inst_year_sid = s.inst_year_sid
and s.demo = 0
and s.state = 1
where i.state = 1
and iy.year_sid = 16
and a.account_sid in (20187987, 20188576, 20188755, 52317128, 20189249)
group by a.name;
count() does not count null values, which s.student_sid will be if no rows join from student.
You need to LEFT JOIN and then SUM() over the group where s.student_sid is not null:
select
a.name,
sum(case when s.student_sid is null then 0 else 1 end) as student_count
from account a
join inst i on a.inst_sid = i.root_inst_sid
join inst_year iy on i.inst_sid = iy.inst_sid
left join student s
on iy.inst_year_sid = s.inst_year_sid
and s.demo = 0
and s.state = 1
where i.state = 1
and iy.year_sid = 16
and a.account_sid in (20187987, 20188576, 20188755, 52317128, 20189249)
group by a.name;
This is assuming that all of the fields in the student table that you are filtering on are optional. If you don't want to enforce removal of records where, say, s.state does not equal 1, then you need to move the s.state=1 predicate into the WHERE clauses.
If, for some reason, you are getting duplicate student IDs and students are being counted twice, then you can change the aggregate function to this:
count(distinct s.student_id) as student_count
...which is safe to do as count(distinct ...) ignores null values.

Major issues with a query

I have a query
SELECT
ZEML.ICC_CODE AS ICC_CODE
,SUM(CS.TOT_HOURS) AS TOT_HOURS
,SUM(CS.NUM_INCIDENT_ALL) AS NUM_INCIDENTS
,(VALUE(FLOAT(SUM(CS.NUM_INCIDENT_ALL)) * 200000 / SUM(TOT_HOURS)
,0)) AS INC_RATE
FROM TR.CLAIMS_SUMM CS
INNER JOIN TR.LOCATION_MASTER LM
ON LM.LOCATION = CS.LOCATION
AND CS.LOCATION < '900'
LEFT JOIN TR.LOCATION_ASSIGNMENTS DISTRICT
ON DISTRICT.LOCATION = LM.LOCATION
AND DISTRICT.ASSIGNMENT_TYPE = 'District'
LEFT JOIN TR.LOCATION_ASSIGNMENTS TERRITORY
ON TERRITORY.LOCATION = LM.LOCATION
AND TERRITORY.ASSIGNMENT_TYPE = 'Territory'
LEFT JOIN TR.EMPL_CLAIMS ZEML
ON CS.LOCATION = ZEML.LOCATION
AND ZEML.TYPE = 'WC'
AND ZEML.STATUS <> 'V'
AND ZEML.CLAIM_ACTION NOT IN ('D','F','I','H')
WHERE CS.DW_DATE BETWEEN '01/01/2014'
AND '05/31/2014'
AND (MONTH(ZEML.DATE_OF_INCIDENT) = MONTH(CS.DW_DATE)
AND YEAR(ZEML.DATE_OF_INCIDENT) = YEAR(CS.DW_DATE))
GROUP BY ZEML.ICC_CODE
UNION
SELECT
'OTHER' AS ICC_CODE
, 0 AS TOT_HOURS
, 0 AS NUM_INCIDENTS
, 0 AS INC_RATE
FROM SYSIBM.SYSDUMMY1
WHERE 1 = 1
ORDER BY 1
in my union where I made an other I want to select everything else from the tr.empl_claims table and store it in the other from the union because this is what I have many other ICC codes without incidents on them and I am doing calculations on our incident rate and hourse based off of all the data but my query right now is only selecting the ones that currently is having incidents which is throwing off my calculations.
From your use of FROM SYSIBM.SYSDUMMY1 I believe you are using DB2 database. If yes, you can use CTE (common table expression) to achieve the desired result like
WITH cte1 AS
(
SELECT
ZEML.ICC_CODE AS ICC_CODE
,SUM(CS.TOT_HOURS) AS TOT_HOURS
,SUM(CS.NUM_INCIDENT_ALL) AS NUM_INCIDENTS
,(VALUE(FLOAT(SUM(CS.NUM_INCIDENT_ALL)) * 200000 / SUM(TOT_HOURS)
,0)) AS INC_RATE
FROM TR.CLAIMS_SUMM CS
... <rest of the code> ...
)
select * from cte1
UNION ALL
SELECT
ICC_CODE
, 0 AS TOT_HOURS
, 0 AS NUM_INCIDENTS
, 0 AS INC_RATE
FROM TR.EMPL_CLAIMS
WHERE ICC_CODE NOT IN
(
SELECT distinct ICC_CODE
FROM cte1
)
ORDER BY 1
SideNote: You are joining the same table LOCATION_ASSIGNMENTS twice (as below) which is not needed.
LEFT JOIN TR.LOCATION_ASSIGNMENTS DISTRICT
ON DISTRICT.LOCATION = LM.LOCATION
AND DISTRICT.ASSIGNMENT_TYPE = 'District'
LEFT JOIN TR.LOCATION_ASSIGNMENTS TERRITORY
ON TERRITORY.LOCATION = LM.LOCATION
AND TERRITORY.ASSIGNMENT_TYPE = 'Territory'
This can be transformed to below using a IN operator
LEFT JOIN TR.LOCATION_ASSIGNMENTS DISTRICT
ON DISTRICT.LOCATION = LM.LOCATION
AND DISTRICT.ASSIGNMENT_TYPE IN ('District', 'Territory')
See more about Common Table Expression in DB2 Here.
Hope this helps.