SQL Query Not Working Properly With Multiple Join Clauses - sql

I have been working on this Query for quite a while. I just got a resolution for part of it and now trying to add that query to my big one, I am having some problems with the output of the query.
SELECT T1.[miles],
T2.[name],
T2.[code],
T2.[routesid],
T2.[id],
T2.[company],
T2.[contract],
T2.[supplier],
T3.[tons],
CASE
WHEN T4.miles IS NULL THEN T5.miles
ELSE T4.miles
END MILES2,
CASE
WHEN T4.miles = T1.miles
AND T4.miles != 9999 THEN T4.flatrate
ELSE T5.rate
END AS RATE,
T3.[change]
FROM ((table1 AS T1
JOIN table2 AS T2
ON T1.[company] = T2.[company]
AND T1.[id] = T2.[routesid])
JOIN table3 AS T3
ON T1.[company] = T3.[code])
LEFT JOIN table4 AS T4
ON T1.[company] = T4.[code]
AND ( T1.[miles] = T4.[miles] )
INNER JOIN (SELECT TOP 1 code,
miles,
rate,
flatrate
FROM table4
WHERE miles = 9999
AND active = 1) AS T5
ON T1.[company] = T5.[code]
WHERE T2.[active] = 1
AND T2.[expiration] < '02/10/2015'
AND T1.[miles] > 0
AND T1.[company] = 'COMPANY'
AND T3.[active] = 1;
I know its a big query. I am trying to figure out why it leaves out the numbers that would pair with the miles on table 4 where it pair with 9999. It has no problem printing out these.
MILES NAME CODE ROUTESID ID COMPANY CONTACT SUPPLIER TONS MILES2 CHANGE
140 N/A N/A 3425 185 ILLINI TARIFF 1 N/A 24 140 Weekly
144 N/A N/A 4532 198 ILLINI TARIFF 3 N/A 24 144 Weekly
9999 N/A N/A 2134 150 ILLINI TARIFF 2 N/A 24 9999 Weekly
Its not printing out right now if it matches this case below. Which I got the SQL statement from my previous stack overflow, I got the SQL statement to print if Miles and Miles2 don't match but since I added the SQLs together it stopped working.
MILES NAME CODE ROUTESID ID COMPANY CONTACT SUPPLIER TONS MILES2 CHANGE
140 N/A N/A 3420 170 ILLINI TARIFF 4 N/A 24 9999 Weekly
Previous Stack Overflow
Here is a demo of the issue
As you can see the 250, 300, 350, 400, 450, and 500 out of TABLE1 isn't displaying.
Here is the resolution

The answer given by Giorgos in your previous Stack Overflow thread worked partially because you only had one company in your example. See this portion of the query?:
INNER JOIN (SELECT TOP 1 code,
miles,
rate,
flatrate
FROM table4
WHERE miles = 9999
AND active = 1) AS T5
ON T1.[company] = T5.[code]
This literally takes only the top row from the table (where miles = 9999 and active = 1) and then tries to join it to the T1 table. Not the top row for each company, just the top row. So whatever company happens to be on the top row is the only one available when you join the T5 table to the T1 table on the company field. You could get around this perhaps by using distinct instead of top 1, if the table doesn't have multiple rows for a single code where miles = 9999 and active = 1 (which I'm guessing you shouldn't, as that would mean there are multiple rates for the same code and miles):
INNER JOIN (SELECT distinct code, miles, rate, flatrate
FROM table4
WHERE miles = 9999 AND active = 1
GROUP BY code) AS T5 ON T1.[company] = T5.[code]
But as others have noted, it is very difficult to understand what you are trying to accomplish, partially because the formatting of your query is odd. And it would help perhaps to re-explain the nature of your tables and the solution you are seeking, as well as defining the problem better.

Related

T-SQL subselect statement is returning all rows instead of limiting to 1 based on subselect

I am trying to return just the first row where the BLOCK_STOP_ORDER = 2. What is wrong with my SQL? Why isn't WHERE SCHEDULE.BLOCK_STOP_ORDER = (SELECT MIN(S1.BLOCK_STOP_ORDER....
working? When I run the subselect on its own it returns the value '2' - doesn't that mean it should then limit the query result to only the row(s) where BLOCK_STOP_ORDER = 2?
SELECT ROUTE.ROUTE_ABBR, SCHEDULE.ROUTE_DIRECTION_ID, SCHEDULE.PATTERN_ID, SCHEDULE.BLOCK_STOP_ORDER,
SCHEDULE.SCHEDULED_TIME, GEO_NODE.GEO_NODE_ABBR, TRIP.TRIP_SEQUENCE AS TPST
FROM SCHEDULE
INNER JOIN GEO_NODE ON SCHEDULE.GEO_NODE_ID = GEO_NODE.GEO_NODE_ID
INNER JOIN ROUTE ON SCHEDULE.ROUTE_ID = ROUTE.ROUTE_ID
INNER JOIN TRIP ON SCHEDULE.TRIP_ID = TRIP.TRIP_ID
WHERE (SCHEDULE.CALENDAR_ID = '120221024') AND ROUTE.ROUTE_ABBR = '001'
AND SCHEDULE.ROUTE_DIRECTION_ID = '2' AND SCHEDULE.PATTERN_ID = '270082'
AND TRIP.TRIP_SEQUENCE = '18600'
AND SCHEDULE.BLOCK_STOP_ORDER =
(SELECT MIN(S1.BLOCK_STOP_ORDER)
FROM SCHEDULE S1
WHERE SCHEDULE.CALENDAR_ID = S1.CALENDAR_ID
AND SCHEDULE.ROUTE_ID = S1.ROUTE_ID
AND SCHEDULE.ROUTE_DIRECTION_ID = S1.ROUTE_DIRECTION_ID
AND SCHEDULE.PATTERN_ID = S1.PATTERN_ID
AND SCHEDULE.SCHEDULED_TIME = S1.SCHEDULED_TIME
AND SCHEDULE.GEO_NODE_ID = S1.GEO_NODE_ID
AND SCHEDULE.BLOCK_STOP_ORDER = S1.BLOCK_STOP_ORDER
AND SCHEDULE.TRIP_ID = S1.TRIP_ID
)
GROUP BY ROUTE.ROUTE_ABBR, SCHEDULE.ROUTE_DIRECTION_ID,
SCHEDULE.PATTERN_ID, SCHEDULE.SCHEDULED_TIME,
GEO_NODE.GEO_NODE_ABBR, SCHEDULE.BLOCK_STOP_ORDER, TRIP.TRIP_SEQUENCE
ORDER BY ROUTE.ROUTE_ABBR, SCHEDULE.ROUTE_DIRECTION_ID, TRIP.TRIP_SEQUENCE
Results:
ROUTE_ABBR
ROUTE_DIRECTION_ID
PATTERN_ID
BLOCK_STOP_ORDER
SCHEDULED_TIME
GEO_NODE_ABBR
TPST
001
2
270082
2
18600
1251
18600
001
2
270082
3
18600
1346
18600
001
2
270082
5
18720
1123
18600
001
2
270082
6
18720
11372
18600
001
2
270082
4
18720
1570
18600
001
2
270082
8
18780
11373
18600
This is probably better solved with the row_number() windowing function:
SELECT *
FROM (
SELECT DISTINCT r.ROUTE_ABBR, s.ROUTE_DIRECTION_ID, s.PATTERN_ID, s.BLOCK_STOP_ORDER,
s.SCHEDULED_TIME, g.GEO_NODE_ABBR, t.TRIP_SEQUENCE AS TPST,
row_number() over (order by SCHEDULE.BLOCK_STOP_ORDER) rn
FROM SCHEDULE s
INNER JOIN GEO_NODE g ON s.GEO_NODE_ID = g.GEO_NODE_ID
INNER JOIN ROUTE r ON s.ROUTE_ID = r.ROUTE_ID
INNER JOIN TRIP t ON s.TRIP_ID = t.TRIP_ID
WHERE s.CALENDAR_ID = '120221024' AND r.ROUTE_ABBR = '001'
AND s.ROUTE_DIRECTION_ID = '2' AND s.PATTERN_ID = '270082'
AND t.TRIP_SEQUENCE = '18600'
) t1
WHERE rn=1
ORDER BY t1.ROUTE_ABBR, t1.ROUTE_DIRECTION_ID, t1.TRIP_SEQUENCE
The problem with the original is the name SCHEDULE. For the full version of the query, the subquery is matching the name in the nested select with the instance of the table from the outer select. This correlates the results of the inner table with the outer, so only the item from that row of the outer table is eligible.
When you run the inner query by itself, separate from the outer query, there is only the one instance of the table. In that situation the WHERE conditions are matching the table to itself — they are always true — and you just get the smallest value of all the rows: 2.
This is why you should ALWAYS give ALL the tables in your queries an alias, and ONLY reference them by that alias (as I did in my answer). Do this, and the MIN() version can work... but will still be slower and more code than using row_number().
Finally, the use of DISTINCT / GROUP BY with every SELECT column is usually an indicator you don't fully understand the JOIN relationships used in the query, and in at least one case the join conditions are not sufficiently selective. I'd hesitate to move a query like that to production, even if it seems to be working, though I confess most of us have done it at some point anyway.

Combining SQL Tables for one source of information

I'm trying to combine 3 different tables into one single row in a query but having some problems. Currently I have four tables; PaintSched, Painted_Log, Paint_Defect, and Paint_Inspection.
PaintSched - Single entry, when scheduler just schedules some parts to be painted
LOT QTY
1 150
2 100
Painted_Log - The paint department then takes the lot and says how many they were able to paint
LOT(FK) QTYPainted
1 145
2 100
Paint_Defect - Master List of defects for paint inspection after parts have been painted. We hand inspect all of our parts that we paint for quality.
DID Defect
1 Scratch
2 Paint Run
Paint_Inspection - Everytime a defect is found the inspector hits a correlating button and the following gets logged. Lot is FK and DID stands for Defect ID from Paint_Defect. QTY is always 1
Lot(FK) DID(FK) QTY
1 1 1
1 1 1
1 2 1
1 1 1
2 2 1
1 2 1
2 1 1
What I'm trying to get is the following output:
Lot Sched Painted Scratch Paint Run
1 150 145 3 2
2 100 100 1 1
I've tried the following to no avail:
SELECT PaintSched.Scheduled, PaintSched.Lot, PaintSched.qty, PaintSched.Is_Painted, Painted_Log.falloff
FROM PaintSched
INNER JOIN Painted_Log ON PaintSched.Lot = Painted_Log.lot
INNER JOIN MPA_Desc ON MPA_Desc.MPAID = PaintSched.MPAID
inner JOIN (
SELECT lot, sum(Paint_Inspection.qty) as seds
FROM Paint_Inspection
WHERE Paint_Inspection.Status = '1'
) AS seeds ON PaintSched.Lot = Paint_Inspection.Lot
SELECT
PS.Lot,
PS.Qty Sched,
Painted,
Scratch,
PaintRun
FROM PaintSched PS
LEFT JOIN (SELECT
Lot,
SUM(QTYPainted) Painted
FROM Painted_Log GROUP BY Lot) PL
ON PS.Lot = PL.Lot
LEFT JOIN (SELECT
Lot,
SUM(CASE WHEN DID = 1 THEN 1 ELSE 0 END) Scratch,
SUM(CASE WHEN DID = 2 THEN 1 ELSE 0 END) PaintRun
FROM Paint_Inspection GROUP BY Lot) PI
ON PS.Lot = PI.Lot
Try that in SQL Fiddle
The code above uses conditional sum to roll up the defect count by type before joining that to the lot id. If you have more than 2 statuses, you will need to update the above code accordingly.
Two things:
You should have a group by in the subquery
When you alias the subquery, you don't join it properly
See the edits to your query below:
SELECT
PaintSched.Scheduled,
PaintSched.Lot,
PaintSched.qty,
PaintSched.Is_Painted,
Painted_Log.falloff
FROM PaintSched
INNER JOIN Painted_Log ON PaintSched.Lot = Painted_Log.lot
INNER JOIN MPA_Desc ON MPA_Desc.MPAID = PaintSched.MPAID
INNER JOIN (
SELECT lot, sum(Paint_Inspection.qty) as seds
FROM Paint_Inspection
WHERE Paint_Inspection.Status = '1'
GROUP BY Paint_Inspection.lot -- Missing GROUP BY
) AS seeds
ON PaintSched.Lot = seeds.Lot

How to find non-matching records between 2 tables not having any unique keys

I have 2 tables tblBudget and tblActuals.
tblBudget
ProjID ExpenseType OrigBudget
101 Furniture 5000
102 Hardware 2000
102 Software 3500
tblActuals
ProjID ExpenseType ActualExpense
101 Furniture 4000
101 Hardware 2500
102 Hardware 1500
I want to pull the matching and non-matching records into a single query so that it shows like this
ProjID ExpenseType OriginalBudget ActualExpense
101 Furniture 5000 4000
101 Hardware 0 2500
102 Hardware 2000 1500
102 Software 3500 0
I tried a join query which successfully selects the matching records but having trouble in selecting the non-matching records. My table does not have primary keys nor can they be added since its part of a huge company db.
Any help from left join experts will be highly appreciated
In mysql use LEFT/RIGHT JOIN to emulate FULL OUTER JOIN
SQL DEMO
SELECT tB.ProjID, tB.ExpenseType, tB.OrigBudget as OrigianlBudget,
COALESCE(tA.ActualExpense,0) as ActualExpense
FROM tblBudget tB
LEFT JOIN tblActuals tA
ON tB.`ExpenseType` = tA.`ExpenseType`
AND tB.`ProjID` = tA.`ProjID`
UNION
SELECT COALESCE(tB.ProjID, tA.ProjID),
COALESCE(tB.ExpenseType, tA.ExpenseType),
COALESCE(tB.OrigBudget, 0),
tA.ActualExpense
FROM tblBudget tB
RIGHT JOIN tblActuals tA
ON tB.`ExpenseType` = tA.`ExpenseType`
AND tB.`ProjID` = tA.`ProjID`
ORDER BY `ProjID`
OUTPUT
MS Access does not make this easy. But here is one approach that should do what you want:
select b.ProjId, b.ExpenseType, b.OriginalBudget,
nz(a.ActualExpense, 0) as ActualExpense
from tblBudget as b left join
tblActual as a
on b.ProjID = a.ProjId and b.ExpenseType = a.ExpenseType
union all
select a.ProjId, a.ExpenseType, 0 as OriginalBudget, a.ActualExpense
from tblActual as a left join
tblBudget as b
on b.ProjID = a.ProjId and b.ExpenseType = a.ExpenseType
where b.ProjId is null

How to get count of installments that customers paid or must paid in sql

I want to get the count of installments that customers already paid for each bill and how many installments they should pay either they paid parts of the installments or they didn't. I tried this code but I couldn't get the real result either I get paid installments of bill or wrong counts of paid installments.
SELECT
sb.op_id ,
sb.auto_bill_no as sales_bill_no ,
sb.installmnts_cont ,
sb.installmnts_cont - COUNT(cupa.installment_no) as installmnts_net ,
sb.bill_total
FROM sale_bill as sb
LEFT OUTER JOIN custmrs_paymnts as cupa ON
sb.installmnt = 1 AND
sb.installmnt_completed = 0 --AND sb.op_id = cupa.sales_bill_no
WHERE cupa.installment = 1
AND sb.custmr_id = #cstmr_no
AND cupa.custmr_id = #cstmr_no
group by sb.op_id , sb.auto_bill_no , sb.installmnts_cont , sb.bill_total
Sample data from sale_bill table :
op_id auto_bill_no installmnts_cont installmnt_completed bill_total
---------------------------------------------------------------------------
55 10 2 true 6000
56 11 2 false 4000
Sample data from custmrs_paymnts table :
money_amt installment sales_bill_no installment_no
-----------------------------------------------------------------
3000 true 55 1
3000 true 55 2
The desired results are:
sales_bill_no installmnts_cont installmnts_net bill_total
---------------------------------------------------------------------
55 2 0 6000
56 2 2 4000
I do see a problem with your join conditions. Like there really aren't any. I'm not sure if this will fix your problem but it is worth a try:
SELECT . . .
FROM sale_bill sb LEFT OUTER JOIN
custmrs_paymnts cupa
ON sb.custmr_id = cupa.custmr_id AND
cupa.installment = 1
WHERE sb.custmr_id = #cstmr_no AND
sb.installmnt = 1 AND
sb.installmnt_completed = 0
Some rules:
Tables should normally be joined on columns in the tables, in the ON clause.
Conditions on the first table of a LEFT JOIN should go in the WHERE.
Conditions on the second table of a LEFT JOIN should go in the ON.

Sum Values within 3 tables

Table 1
jh."job-hdr"
job-date job-disp job-dept job-route job-id job-no
01/04/2013 6467 abc 123 22 81088
01/04/2013 6468 abc 987 36 82568
Table 2
rh."rec-charge"
charge-type rec-id base-sales-value
XYZ 22 700
Table 3
rc."rec-cost"
charge-type rec-id base-cost-value
XYZ 22 300
I need to be able to get the profit from this jobid of
700 - 300 = 400
This is where I have gotten up to
SELECT jh."job-date", jh."job-disp", jh."job-dept", jh."job-route", rc."charge-type",rh."charge-type",
SUM(rc."base-cost-value") as COSTS,
SUM(rh."base-sales-value") as SALES,
SUM(rh."base-sales-value") - SUM(rc."base-cost-value") as PROFIT
FROM MSN.PUB."rec-chg" rh, PUB."job-hdr" jh, pub."rec-cost" rc
WHERE jh."job-date" between '2013-04-01' and '2013-04-30'
and jh."job-id" = rc."rec-id"
and rc."rec-id" = rh."rec-id"
and jh."grp-id" = '0'
and jh."job-status"<>'D'
and jh."job-no" = '81088'
and rc."charge-type" = rh."charge-type"
Group by jh."job-date", jh."job-disp", jh."job-dept", jh."job-route",rc."charge- type",rh."charge-type"
This is not giving me great results at all and I know I am way off. I just need to be put in the right direction.
Update profit to:
SUM(rh."base-sales-value" - rc."base-cost-value") as PROFIT
And update your group by to:
group by jh."job-id", rc."rec-id", rh."rec-id"
This should give your the desired result (hopefully). Sorry didnt not have time to test it myself. The main focus is on group by, which should be applied on a field that would return multiple results for other fields you want to run the sum on.
Your question appears is a little ambiguous, as to whether you want the results by job or by charge type. In either case, you need to aggregate the results before doing the join. The following query does this at the job level:
SELECT jh."job-date", jh."job-disp", jh."job-dept", jh."job-route",
COSTS, SALES, SALES - COSTS as PROFIT
FROM PUB."job-hdr" jh left outer join
(select rh."rec-id", SUM(rh."base-sales-value") as SALES
from MSN.PUB."rec-chg" rh
group by rh."rec-id"
) rh
on jh."job-id" = rh."rec-id" left outer join
(select rc."rec-id", SUM(rc."base-cost-value") as COSTS
from pub."rec-cost" rc
group by rc."rec-id"
) rc
on jh."job-id" = rc."rec-id"
WHERE jh."grp-id" = '0' and
jh."job-status" <> 'D' and
jh."job-no" = '81088';
Notice that I replaced your implicit join syntax with explicit join syntax. The explicit version is much better, so you should learn to use it.