SQL Count and Group by derived logic (Db2) - sql

This code works. However, I want to run it without the filter for contract_No and then it doesn't work:-(. I'm sure im missing a left join and some steps.
Basically I have Contracts and contract lines (same table - Contract_repo), and I want to create a grouping of contract lines based on how many payments they have had last 12 months (different table on another level - Payments - and then count contract lines per group and rolled up to contract_No level.
Select a.Contract_No,count (*) as "Active Contract Lines",
(select count(distinct b.Contract_Line_No)
from Payments b
where b.Contract_NO = 32778
and b.Contract_Line_No=(
select distinct
case when Count(
Case when MONTHS_BETWEEN((CURRENT DATE - 1 MONTH),c.Period) < 11
then 1 end
)=0
then c.Contract_Line_No end
)
from Payments c
where c.Contract_No = b.Contract_No
group by c.Contract_Line_No
) as "Contract_Line = 0 trx L12M"
from Contract_Repo a
where a.Contract_NO = 32778
and a.status = 1
group by a.Contract_NO
This is my desired output:
Contract_No
Active Lines
Lines with zero trx
Lines with 12 trx
12345
1000
600
400
67894
100
10
90
101112
500
100
400

Related

Geting sum for main and sub-main categories from three tables in SQLite database

I have three tables in SQLite database; the first table (PROJWBS) describes the project works and sub-works like this:
PROJ_ID
WBS_ID
PARENT_WWBS_ID
WBS_NAME
1
1
0
MAIN WORK
1
10
1
WORK-01
1
11
1
WORK-02
The second table (TASK) describes the work tasks:
TASK_ID
PROJ_ID
WBS_ID
TASK_NAME
1
1
10
Tiling
2
1
10
Metal Works
3
1
11
Wood Works
And the third table (TASKRSRC) describes the tasks target cost:
TASK_ID
PROJ_ID
TARGET_COST
1
1
500
1
1
750
2
1
350
3
1
150
I have created this query to order the first table by work & sub-works arrangement and calculate the works & sub-works cost :
SELECT
PROJWBS.Wbs_id, PROJWBS.Parent_Wbs_id, PROJWBS.Wbs_name,
COALESCE(subquery.Total_Cost, 0) AS Total_Cost
FROM
PROJWBS
LEFT JOIN
(SELECT
TASK.Wbs_id, SUM(TASKRSRC.Target_Cost) AS Total_Cost
FROM
TASK
JOIN
TASKRSRC ON TASK.Task_id = TASKRSRC.Task_id
GROUP BY
TASK.Wbs_id) AS subquery ON PROJWBS.Wbs_id = subquery.Wbs_id
WHERE
PROJ_ID = 1
GROUP BY
PROJWBS.Wbs_id, PROJWBS.Parent_Wbs_id, PROJWBS.Wbs_name
ORDER BY
CASE
WHEN PROJWBS.Wbs_id = PROJWBS.PARENT_WBS_ID THEN PROJWBS.PARENT_WBS_ID
WHEN PROJWBS.Wbs_id < PROJWBS.PARENT_WBS_ID THEN PROJWBS.WBS_ID
WHEN PROJWBS.Wbs_id > PROJWBS.PARENT_WBS_ID THEN PROJWBS.PARENT_WBS_ID
END;
The resulting table displays only 0 values ​​for the cost of the main works and calculates the cost only for the subworks.
Note: the main tasks have nothing to do with the task table, while the subtasks has.
The query should return a result like this (taking by consideration the order condition inside my query ):
WBS_ID
WBS_NAME
Total_Cost
1
MAIN WORK
1750
10
WORK-01
1600
11
WORK-02
150
Solution will be appreciated.
Consider a second aggregate subquery to sum total cost at project level:
SELECT p.WBS_ID, p.PARENT_WBS_ID, p.WBS_NAME,
COALESCE(main_agg.TOTAL_COST, sub_agg.TOTAL_COST, 0) AS TOTAL_COST
FROM PROJWBS p
LEFT JOIN (
SELECT PROJ_ID, SUM(TARGET_COST) AS TOTAL_COST
FROM TASKRSRC
GROUP BY PROJ_ID
) AS main_agg
ON p.PROJ_ID = main_agg.PROJ_ID
AND p.PARENT_WBS_ID = 0
LEFT JOIN (
SELECT TASK.WBS_ID, SUM(TASKRSRC.TARGET_COST) AS TARGET_COST
FROM TASK
JOIN TASKRSRC ON TASK.TASK_ID = TASKRSRC.TASK_ID
GROUP BY TASK.WBS_ID
) AS sub_agg
ON p.WBS_ID = sub_agg.WBS_ID
WHERE p.PROJ_ID = 1
ORDER BY ... ;

How to create a Select statement which contains a SUM on a different table

I currently have a select statement that is causing me issues, i have two tables:
Customer table
ID Month4Value Month5Value
1 24 5
Orders table
ID Year Month Value Quantity
1 2018 8 10 2
1 2018 4 2 1
1 2018 6 10 4
1 2018 4 7 3
I currently have the below view:
Create View Values as
Select ID, Year, Month, ROUND(SUM(Value*Quantity),2) as NewQuantity
FROM Orders
GROUP BY ID, Year, Month
The below select statement is what i am trying to run
Select Customer.ID, Customer.Month5Value, NewQuantity
from Customer inner join Values on Customer.ID = Values.ID
where ROUND(Customer.Month5Value, 2) <> ROUND(NewQuantity,2)
AND Values.Year = 2018
AND Values.Month = 5
What i am trying to achieve is to find any mismatches between the Orders table and the Customer table. In the above example, what i am expecting is to highlight that the value in Customer.Month5Value does not match the total of the (Quantity*Value) from the Orders table.
As there are 0 orders for Month 5 in the Orders Table, the Month5Value should be 0. However, it returns no entrys.
Any thoughts about what i have missed?
EDIT -
I have updated my query to this:
Select Customer.ID, Customer.Month5Value, NewQuantity
from Customer left join Values on Customer.ID = Values.ID
where ROUND(Customer.Month5Value, 2) <> ISNULL((Select NewQuantity from Customer left join Values on Customer.ID = Values.ID where Values.Month = 5 and Values.Year = 2018),0)
This has given me a list of IDs which have an incorrect amount in Month5Value on the Customer table, but displays lines for each month entry
ID Month5Value NewQuantity
1 5 24
1 5 40
1 5 20
How can i adjust this so that I get one line per ID with the correct value for NewQuantity (either 0 or NULL in this case)?
I think the INNER JOIN is removing any records which are missing from VALUES. Replacing the INNER JOIN with LEFT JOIN may give the result you are looking for.

SQL: Using one table as a criteria for itself

I am trying to use a parameter of a table as a criteria for itself, and can't quite get my sql statement right. It seems to be a relatively simple query; I'm using a sub query for my criteria, but it is not filtering out other rows on my table.
Background:
Manufacturing production floor: I have a bunch of machinists on their machines right now running an operation (OprSeq) of a job (JobNum). From the LaborDtl table, which keeps a record of all labor activity, I can see what labor is currently active (ActiveTrans = 1). With this criteria of active labor, I want to sum up all the past labor transactions on each active labor entry. So I need a LaborDtl table of inactive labor activity with the criteria of active labor from the same table.
The code:
Heres my 'criteria' subquery:
SELECT
LaborDtl.JobNum,
LaborDtl.OprSeq
FROM Erp.LaborDtl
WHERE LaborDtl.ActiveTrans = 1
Which returns active transactions, here's the first couple (sorted by job):
Job Operation
000193 90
000457 70
000457 70
020008-1 140
020008-2 130
020010 60
020035 130
020175 40
020175-2 50
020186 80
020199 10
020203 50
020212 40
020258 60
020272 10
020283 30
020298 10
020299 30
Then here's the full SQL Statement, with the query above embedded:
SELECT
LaborDtl.JobNum,
LaborDtl.OprSeq as "Op",
SUM(LaborDtl.LaborQty) as "Total Labor"
FROM Erp.LaborDtl
WHERE EXISTS
(
SELECT
LaborDtl.Company,
LaborDtl.JobNum,
LaborDtl.OprSeq
FROM Erp.LaborDtl
WHERE LaborDtl.ActiveTrans = 1 --Labor table of just current activity
)
GROUP BY LaborDtl.JobNum, LaborDtl.OprSeq
I expect to see only the Job and Operation numbers that exist in my sub query, but I'm getting both jobs and operations that don't exist in my sub query. Here are the first 10 (note, the first JobNum should be 000193 per my criteria)
JobNum Op Total Labor
0 0.00000000
000004 1 32.00000000
000019 1 106.00000000
000029 1 175.00000000
000143 1 85.00000000
000164 1 58.00000000
000181 1 500.00000000
000227 1 116.00000000
000421 1 154.00000000
000458 1 67.00000000
You're missing some condition to tie the outer and inner queries together. Right now, without that criteria, the inner query just returns "true", as there are jobs with active activities and thus all the rows in the outer query are returned. Note that you'll have to add aliases to the tables, as the inner and outer query use the same table:
SELECT a.JobNum, a.OprSeq as "Op", SUM(a.LaborQty) as "Total Labor"
FROM Erp.LaborDtl a
WHERE EXISTS (SELECT * -- The select list doesn't really matter here
FROM Erp.LaborDtl b
WHERE a.JobNum = b.JobNum AND -- Here!
a.OprSeq = b.OprSeq AND -- And here!
b.ActiveTrans = 1 -- Labor table of just current activity
)
GROUP BY a.JobNum, a.OprSeq
Note, however, that there's an easier (IMHO) way. Since you're grouping by JobNum and OprSeq anyway, you could just count the number of active transactions and using a having clause to query only those that have at least one active transaction:
SELECT JobNum, OprSeq as "Op", SUM(LaborQty) as "Total Labor"
FROM Erp.LaborDtl
GROUP BY JobNum, OprSeq
HAVING COUNT(CASE ActiveTrans WHEN 1 THEN 1 END) > 0
Without knowing RDBMS vendor and version this is the best I can do:
SELECT
t1.JobNum,
t1.OprSeq as "Op",
SUM(t1.LaborQty) as "Total Labor"
FROM Erp.LaborDtl t1
WHERE EXISTS
(
SELECT 1
FROM Erp.LaborDtl t2
WHERE t2.ActiveTrans = 1 --Labor table of just current activity
and t2.Company = t1.Company
and t2.JobNum = t1.JobNum
and t2.OprSeq = t1.OprSeq
)
GROUP BY t1.JobNum, t1.OprSeq

Summing two tables into one table in Jet SQL

Another SQL question if I may.
I have two tables (generated from TRANSFORM - PIVOT queries in Jet SQL)
Category ID Account Jan Feb ... Dec
1 1 Cash 10 20 30
1 2 Card 100 200 300
1 3 Savings 200 400 600
and
Category ID Account Jan Feb ... Dec
1 1 Cash -5 -10 -20
1 2 Card -100 -200
1 3 Savings -100 -400
Category, ID and Account will always be the same in two tables. There will be no accounts that occur in one table that don't occur in others. There may be NULL values in either table but there will always be a matching cell in each table.
What I would like is
Category ID Account Jan Feb ... Dec
1 1 Cash 5 10 10
1 2 Card 100 100 100
1 3 Savings 100 400 200
I have played around with UNION and JOIN queries but can't get there.
Thanks again,
Andy
Edit - The original queries are
TRANSFORM Sum(Items.amount) AS total
SELECT Accounts.accCategory, Accounts.ID, Accounts.comment AS Account
FROM Accounts INNER JOIN Items ON Accounts.ID = Items.accFrom
WHERE (((Year([idate]))=2013) AND ((Items.category)<>3 Or (Items.category) Is Null) AND ((Accounts.accCategory)=6 OR (Accounts.accCategory)=7) AND ((Accounts.curr)=1))
GROUP BY Accounts.accCategory, Accounts.ID, Accounts.comment
PIVOT Format(idate,'mmm') IN ('Jan','Feb','Mar','Apr', 'May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
and
TRANSFORM Sum(Items.amount) AS total
SELECT Accounts.accCategory, Accounts.ID, Accounts.comment AS Account
FROM Accounts INNER JOIN Items ON Accounts.ID = Items.accFrom
WHERE (((Year([idate]))=2013) AND ((Items.category)=3) AND ((Items.comment)='Monthly') AND ((Accounts.accCategory)=6) AND ((Accounts.curr)=1))
GROUP BY Accounts.accCategory, Accounts.ID, Accounts.comment
PIVOT Format(idate,'mmm') In ('Jan','Feb','Mar','Apr', 'May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
One approach using the join of the 2 tables deb and cred is as follows:
SELECT cred.accCategory, cred.ID, cred.Account, [deb].[Jan]+[cred].[Jan] AS tot_Jan
FROM cred INNER JOIN deb ON (cred.Account = deb.Account) AND (cred.ID = deb.ID) AND (cred.accCategory = deb.accCategory);
This screenshot shows how the query is constructed:
And here are the results (excuse the table formatting):
accCategory ID Account tot_Jan
1 1 Cash 5
1 2 Card 100
1 3 Savings 100
Be warned that the JOIN is an equi-JOIN so it means the same number of records must exist in BOTH tables else you will find that credits where a debit does not exist will be dropped! So make sure that both input tables are consistent with the same key values (the 3 GROUP BY fields in your earlier PIVOT query).
The alternative approach is the UNION with a GROUP BY to calculate the total from it. I will paste that shortly.
========== EDIT ============
You can build this up in stages. First I did SELECT * FROM cred UNION SELECT * FROM deb and looked at in Design View.
Went back into the SQL view and made sure the UNION query has brackets around it and SELECT sub.* FROM before the brackets and AS SUB after the brackets.
Then I modified the query to include the GROUP BY and Sum(Jan) bits. The final SQL is here and a screenshot follows.
SELECT sub.accCategory, sub.ID, sub.Account, Sum(sub.Jan) AS SumOfJan
FROM (SELECT * FROM cred UNION SELECT * FROM deb) AS sub
GROUP BY sub.accCategory, sub.ID, sub.Account;

DB2 SQL filter query result by evaluating an ID which has two types of entries

After many attempts I have failed at this and hoping someone can help. The query returns every entry a user makes when items are made in the factory against and order number. For example
Order Number Entry type Quantity
3000 1 1000
3000 1 500
3000 2 300
3000 2 100
4000 2 1000
5000 1 1000
What I want to the query do is to return filter the results like this
If the order number has an entry type 1 and 2 return the row which is type 1 only
otherwise just return row whatever the type is for that order number.
So the above would end up:
Order Number Entry type Quantity
3000 1 1000
3000 1 500
4000 2 1000
5000 1 1000
Currently my query (DB2, in very basic terms looks like this ) and was correct until a change request came through!
Select * from bookings where type=1 or type=2
thanks!
select * from bookings
left outer join (
select order_number,
max(case when type=1 then 1 else 0 end) +
max(case when type=2 then 1 else 0 end) as type_1_and_2
from bookings
group by order_number
) has_1_and_2 on
type_1_and_2 = 2
has_1_and_2.order_number = bookings.order_number
where
bookings.type = 1 or
has_1_and_2.order_number is null
Find all the orders that have both type 1 and type 2, and then join it.
If the row matched the join, only return it if it is type 1
If the row did not match the join (has_type_2.order_number is null) return it no matter what the type is.
A "common table expression" [CTE] can often simplify your logic. You can think of it as a way to break a complex problem into conceptual steps. In the example below, you can think of g as the name of the result set of the CTE, which will then be joined to
WITH g as
( SELECT order_number, min(type) as low_type
FROM bookings
GROUP BY order_number
)
SELECT b.*
FROM g
JOIN bookings b ON g.order_number = b.order_number
AND g.low_type = b.type
The JOIN ON conditions will work so that if both types are present then low_type will be 1, and only that type of record will be chosen. If there is only one type it will be identical to low_type.
This should work fine as long as 1 and 2 are the only types allowed in the bookings table. If not then you can simply add a WHERE clause in the CTE and in the outer SELECT.