How to use main query column value in sub-query? - sql

I have an SQL query where I want to use one of the column value of the main query in the sub query.
The query is:
select **tool.item**, asset.id, tool.date,
(select freq from workorder
where type = 'CP' and itemnum = **tool.item**) freq, asset.pm
from tool,
asset
where too.num = asset.num
and asset.status = 'ACTIVE';
In this query I want use the fetched tool.item value in the sub query.
item assetid date pm freq
A1 1 12-NOV-15 123 freq from workorder where itemnum ='A1'
A2 2 13-NOV-15 124 freq from workorder where itemnum ='A2'
Could you help me with this?
Thanks in advance.

I strongly encourage you to do two things:
Learn proper JOIN syntax (never use commas in the from clause.
Use abbreviations for table aliases.
So, write the query as:
select t.item, a.id, t.date,
(select wo.freq
from workorder wo
where wo.type = 'CP' and wo.itemnum = t.item
) as freq,
a.pm
from tool t join
asset a
on t.num = a.num
where a.status = 'ACTIVE';
A correlated subquery is a query where the subquery uses columns from the outer query. In this case, the correlation uses t.item in the where clause. When using correlated subqueries I very, very, very strongly recommend that you always use table aliases. It is very easy to make mistakes with column names, and these problems can be quite hard to find.

it's similar to normal join, you need join you subquery in column with your tables in from section
if the query returns null or 1 value it works ok
if it returns more than 1 value you will have exception
select tool.item, asset.id, tool.date,
(select freq from workorder
where type = 'CP' and itemnum = tool.item) freq, asset.pm
from tool,
asset
where tool.num = asset.num
and asset.status = 'ACTIVE';

Related

SQL aggregation updates for some but not others

I am running this query which should take the sum of an amount from a table and if it <= 0, update the status of a different table from Active to Deactive. The query updates some values but not others. I have isolated to one observation where there are 3 payments that total 0 where it does not work.(123456789) What could be happening here? I am using sql query in Microsoft Access. Thank you.
UPDATE tbl_MASTER INNER JOIN tbl_Payments ON tbl_MASTER.DeviceID = tbl_Payments.DeviceID SET tbl_MASTER.ActiveDeactive = "DeActive"
WHERE tbl_Payments.Amount=(SELECT SUM(tbl_Payments.Amount) <= 0 FROM tbl_Payments) AND tbl__MASTER = '123456789';
Your query doesn't really make a lot of sense, to be honest. Where you have tbl_Payments.Amount=(SELECT SUM(tbl_Payments.Amount) <= 0 FROM tbl_Payments), that sub-query will just be summing up the "Amount" of every record in the table, regardless of which DeviceID. Plus, you're looking for one record in tbl_Payments table where the Amount = the sum of all of the Amounts in tbl_Payments??
I'd suggest that your query probably needs to be something more like this:
UPDATE tbl_MASTER SET tbl_MASTER.ActiveDeactive = "DeActive"
WHERE (SELECT SUM(tbl_Payments.Amount) FROM tbl_Payments WHERE tbl_Payments.DeviceID = tbl_MASTER.DeviceID) <= 0 AND tbl__MASTER = '123456789';
Currently, the subquery does not correlate specific IDs to outer query and also you specify <= 0 inside subquery's SELECT clause. Consider adjusting for IN clause with logic in a conditional HAVING and use table aliases to distinguish same named tables.
UPDATE tbl_MASTER AS m
INNER JOIN tbl_Payments AS p
ON m.DeviceID = p.DeviceID
SET m.ActiveDeactive = 'DeActive'
WHERE sub_p.DeviceID IN (
SELECT sub_p.DevideID
FROM tbl_Payments AS sub_p
GROUP BY sub_p.DeviceID
HAVING SUM(sub_p.Amount) <= 0
)

SQL Selecting data from 3 tables using GROUP BY

I am trying to write a formula to pull data from 3 tables and struggling to get it working.
I need to use the InventoryNbr from table s and do a group by, because there is many of the same InventoryNbr's, and I just want the MAX IndexListID Returned. The IndexListID is a Unique Key, so when I search on table il, I should only return 1 row. I want to then pull the end year from that row as well as the modelnm, and use those 2 values to get the CarlineNm. Here is my code:
SELECT s.InventoryNbr, MAX(s.IndexListID) AS IndexListID, il.EndYear, c.CarlineNm
FROM sysidla as s
INNER JOIN IndexList as il
ON s.IndexListID = il.IndexListID
INNER JOIN Carline as c
ON il.EndYear = c.CarlineYear
AND il.ModelNm = c.ModelNm
GROUP BY InventoryNbr
ORDER BY InventoryNbr ASC;
The error I keep getting is:
Column 'IndexList.EndYear' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Here is some sample data:
sysidla
InventoryNbr|IndexListID|Junk|Junk2
12345|1|x|y
12345|2|c|r
12345|3|c|e
12346|4|e|w
IndexList
ModelNm|Junk|Junk1|Junk3|EndYear|IndexListID
name1|c|f|r|2004|1
name2|c|f|r|2008|2
name3|c|f|r|2012|3
name4|c|f|r|2004|4
name5|c|f|r|2018|5
Carline
CarlineYear|CarlineNm|Junk9|ModelNm
2005|NAME|d|name1
2012|NAME22|d|name3
2005|NAME354|d|name4
2005|NAME1|d|name5
So for instance, this is an incomplete data sample because every IndexListID will have a match in IndexList, but I want to be able match InventoryNbr 12345 and select the max INDEXLISTID which is 3, then use 3 on IndexList to grab name3 and 2012. Then I want to use 2012 and name3 to get NAME22 From Carine.
Use window functions in a subquery:
SELECT s.InventoryNbr, s.IndexListID, il.EndYear, c.CarlineNm
FROM (SELECT s.*,
ROW_NUMBER() OVER (PARTITION BY il.IndexListID ORDER BY il.IndexListID DESC) as seqnum
FROM sysidla s
) s INNER JOIN
IndexList il
ON s.IndexListID = il.IndexListID INNER JOIN
Carline c
ON il.EndYear = c.CarlineYear AND
il.ModelNm = c.ModelNm
WHERE seqnum = 1
ORDER BY InventoryNbr ASC;
No aggregation is needed.
Your are getting this error because, there is a problem with your group by and SELECT section . You can not select column while using group by like this. Either you have to use an aggregate function or your column should in the group by to select like this.
So,Here is a solution you can check =>
SELECT T.*,il.EndYear, c.CarlineNm FROM
(SELECT s.InventoryNbr, MAX(s.IndexListID) AS IndexListID
FROM sysidla as s
GROUP BY InventoryNbr) T
INNER JOIN IndexList as il ON T.IndexListID = il.IndexListID
INNER JOIN Carline as c ON il.EndYear = c.CarlineYear AND il.ModelNm = c.ModelNm
ORDER BY T.InventoryNbr ASC;
Note: This code is not optimized. Sub-query is little slow however, you can optimize that using window function (CTE). Please check and let me know.

Join SQL Server Showing Duplicate Row

I want to ask something about joining query. I have a query like this:
SELECT b.compilecodingid,
a.subjobfamily,
b.position,
b.nocoding,
( CASE
WHEN (SELECT Count(0)
FROM trlspbia
WHERE learningsystemid = a.learningsystemid
AND compilecodingid = b.compilecodingid
AND moduleid = '2018081616230361362303614'
AND learningroadmap = 'Basic') > 0 THEN 1
ELSE 0
END ) AS CountPickPBIA
FROM trlsplanning a,
trcompilecodingheader b
WHERE a.learningsystemid = b.learningsystemid
AND a.position = b.position
AND a.learningsystemid = '2018081513283162000000001'
order by CountPickPBIA desc
I know it's because Column Position on Table TrLsPlanning has more than 1 data,
Anyone can help me to find the solution?
Thank you.
The simplest solution is probably select distinct:
SELECT cch.compilecodingid, p.subjobfamily, cch.position, cch.nocoding,
(CASE WHEN EXISTS (SELECT 1
FROM trlspbia s
WHERE s.learningsystemid = p.learningsystemid AND
s.compilecodingid = ccb.compilecodingid AND
s.moduleid = '2018081616230361362303614' AND
s.learningroadmap = 'Basic'
)
THEN 1
ELSE 0
END) AS CountPickPBIA
FROM trlsplanning p JOIN
trcompilecodingheader cch
ON p.learningsystemid = cch.learningsystemid AND
p.position = cch.position
WHERE p.learningsystemid = '2018081513283162000000001'
ORDER BY CountPickPBIA DESC;
SELECT DISTINCT incurs its own overhead. But without more information about the structure and contents of the table, this is the simplest solution.
Note other changes in the query:
Table aliases are abbreviations for table names, rather than being arbitrary letters.
The JOIN syntax is fixed, to use modern, proper, and standard JOIN/ON.
All columns are qualified with the table alias, particularly those in the correlated subqueries.
The subquery uses EXISTS rather than COUNT(*). This is both more efficient and it probably better expresses the logic you want.

SQL conditional for a field using multiple subqueries as cases

I am using Proc SQL, but this question should be relevant for all SQL variants. I am trying to populate a field BruceDPOtest with values from two subqueries with if the first query results in blanks--CASE WHEN BruceDPO = INPUT("", 8.) --it fills that blank with another subquery's BruceDPO value:
THEN (
SELECT SUM(PART_QTY) FROM RSCCParts LEFT JOIN DPO.DPO_PART_ORD_HST AS Total
ON RSCCParts.PartID = STRIP(Total.PART_NO_ID)
WHERE PUT(PROC_DT, YY.) LIKE '%2016%' GROUP BY PART_NO_ID) ELSE BruceDPO END
For example, the first query gives the following results;
Part DPO
1234 100
1235
The second subquery that references data that can populate the second row is run to get:
Part DPO
1234 100
1235 999
Here is the full code:
PROC SQL;
CREATE VIEW DPOMergeView AS(SELECT *,
CASE
WHEN BruceDPO = INPUT("", 8.) THEN (
SELECT SUM(PART_QTY) FROM RSCCParts LEFT JOIN DPO.DPO_PART_ORD_HST AS Total
ON RSCCParts.PartID = STRIP(Total.PART_NO_ID)
WHERE PUT(PROC_DT, YY.) LIKE '%2016%' GROUP BY PART_NO_ID)
ELSE BruceDPO
END
AS BruceDPOtest
FROM
RSCCParts
LEFT JOIN (SELECT RSCCParts.PartID AS BrucePartID, BruceDPO, Year
FROM RSCCParts
LEFT JOIN
(SELECT PART_NO_ID AS PartNumber, SUM(PART_QTY) AS BruceDPO, STRIP(YR) AS Year
FROM
DPO.DPO_PART_HST_MAIN
WHERE YR = '2016'
GROUP BY PartNumber, Year) AS FQuery
ON
RSCCParts.PartID = STRIP(FQuery.PartNumber)) AS B
ON RSCCParts.PartID = B.BrucePartID);
QUIT;
As I run this query, it gets stuck on DATA Step and after 30 minutes, I stopped the query. Am I doing this correctly? If there is a better way to do this please let me know!
Normally I avoid correlated subqueries in SQL since it just makes it feel like you are trying to process the data record by record instead of by combining sets. But if you did what to use syntax like
case when (x) then (sub query result) else variable_name end
then the subquery needs to return only one value. Your query
SELECT SUM(PART_QTY)
FROM RSCCParts LEFT JOIN DPO.DPO_PART_ORD_HST AS Total
ON RSCCParts.PartID = STRIP(Total.PART_NO_ID)
WHERE PUT(PROC_DT, YY.) LIKE '%2016%'
GROUP BY PART_NO_ID
looks like it will return multiple observations since you are using a GROUP BY clause.
Shouldn't that subquery look more like
SELECT SUM(Total.PART_QTY)
FROM DPO.DPO_PART_ORD_HST AS Total
WHERE RSCCParts.PartID = STRIP(Total.PART_NO_ID)
AND PUT(PROC_DT, YY.) LIKE '%2016%'
Your query has multiple references to RSCCPARTS table so you might need to introduce an alias to each so that you can clarify which one you want to use to get PARTID from to match to PART_NO_ID.

SQL Select Where or Having

Im attempting to get some records from a table based on certain factors.
One of the factors is simply with fields on the same table, the other is when joining to another table, I want to compare the number of records in the joined table to a field on the first table. Below is a sample code.
select * from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = 1486
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
-- Having COUNT(LC.ID) = D.Lines
Now from the code above I cant have the Count function in the where clause, and I cant have a field in in the having clause without it being in a function.
Im probably missing something very simple here. But I cant figure it out.
Any help is appreciated it.
EDIT: I do apologise should have explained the structure of the tables, the Destinations are single records, which the LiveCalls table can hold multiple records based on the Destinations ID (foreign key).
Thank you very much for everyones help. My final code:
select D.ID, D.Description, D.Lines, D.Active, D.AlternateFail, D.ConfigurationID, COUNT(LC.ID) AS LiveCalls from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = #ConfigurationID
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
GROUP BY D.ID, D.Description, D.Lines, D.Active, D.AlternateFail, D.ConfigurationID
HAVING COUNT(LC.ID) <= D.Lines
The simple thing you're missing is the GROUP BY statement.
As JNK mentioned in the comments below, you cannot use an aggregate function (such as COUNT, AVG, SUM, MIN) if you don't have a GROUP BY clause, unless your SELECT statement only references literal values (and no column names).
Your code should probably be something like:
SELECT <someFields>
FROM tDestinations D
LEFT JOIN tLiveCalls LC on LC.DestinationID = D.ID
WHERE D.ConfigurationID = 1486
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
GROUP BY <someFields>
HAVING COUNT(LC.ID) = D.Lines
Note that you have to specify the selected fields explicitely, in both the SELECT and GROUP BY statements (no * allowed).
you can only use having with aggregations. Actually having is the "where clause" for aggregation, BUT you can still have a where on the columns that you are no aggregating.
For example:
SELECT TABLE_TYPE, COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
where TABLE_TYPE='VIEW'
group by TABLE_TYPE
having COUNT(*)>1
In your case you need to use havving count(*)=1
so, I think your query would be something like this:
select YOUR_COLUMN
from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = 1486 AND (D.Active = 1 AND D.AlternateFail > GETDATE())
group by YOUR_COLUMN
Having COUNT(LC.ID) = value