Nested Subqueries with Group By - sql

Having trouble with this one. Getting syntax error at the arrow. I'm using subqueries on purpose. I'm wondering if it's possible to mix 'where' and 'having'?
;with books_not_ordered as
(
select BK.book_id
from bkinfo.books BK
where BK.book_id not in
(
select OD.book_id
from bkorders.order_details OD
)
)
select AU.author_id, AU.author_name_last
from bkinfo.authors AU
where exists
(
select BAU.author_id, count(*) as NumBooks
from bkinfo.book_authors BAU
group by BAU.author_id
having count(*) > 1
==>where AU.author_id = BAU.author_id
and
BAU.book_id in
(
select BK.book_id
from bkinfo.books BK
where BK.book_id in
(
select cte.book_id
from books_not_ordered cte
)
)
)
;
go

The where clause should come before your group by and having
select BAU.author_id, count(*) as NumBooks
from bkinfo.book_authors BAU
where AU.author_id = BAU.author_id
group by BAU.author_id
having count(*) > 1

Assuming that you are trying to get "The authors of the books that are not in order_details table, but who have written more than one book". The following query should work.
SELECT AU.author_id, AU.author_name_last
FROM bkinfo.books BK
JOIN bkinfo.book_authors BAU ON BAU.author_id = BK.author_id
JOIN bkinfo.authors AU ON BAU.author_id = AU.author_id
LEFT OUTER JOIN bkorders.order_details OD ON BK.book_id = OD.book_id
WHERE OD.book_id IS NULL
GROUP BY AU.author_id, AU.author_name_last
HAVING COUNT(BK.Book_id) > 1

Related

SQL Join SELECT MAX, But also where a value does not exist

I have the following SQL which works - It displays all of the items, along with the MAX starttime.
However, I'd also like to show items that to not have a record in playlistlog - How would one
SELECT items.idx, items.title, items.artist, playlistlog.starttime
FROM items
LEFT JOIN playlistlog ON playlistlog.item = items.idx
WHERE playlistlog.starttime = (
SELECT MAX(starttime)
FROM playlistlog AS pl2
WHERE pl2.item = items.idx
)
The where clause is turning your left join into an inner join.
Use AND instead of WHERE.
SELECT items.idx, items.title, items.artist, playlistlog.starttime
FROM items
LEFT JOIN playlistlog ON playlistlog.item = items.idx
and playlistlog.starttime = (
SELECT MAX(starttime)
FROM playlistlog AS pl2
WHERE pl2.item = items.idx
)
This can be done a bit shorter using standard SQL's window function:
SELECT items.idx, items.title, items.artist, pl.starttime
FROM items
LEFT JOIN (
select item,
starttime,
row_number() over (partition by item order by starttime desc) as rn
from playlistlog
) pl ON pl.item = items.idx AND pl.rn = 1

Oracle query - how to make count to return values with 0

How can I make a count to return also the values with 0 in it.
Example:
select count(1), equipment_name
from alarms.new_alarms
where equipment_name in (
select eqp from ne_db.ne_list)
Group by equipment_name
It is returning only the counts with values higher than 0 , but I need to know the records that are not returning anything.
Any help is greatly appreciated.
thanks,
Marco
Try using LEFT JOIN,
SELECT a.eqp, COUNT(b.equipment_name) totalCount
FROM ne_db.ne_list a
LEFT JOIN alarms.new_alarms b
ON a.eqp = b.equipment_name
GROUP BY a.eqp
If the table ne_list has no duplicates, then you can do a left join. That assumption may not be true, so the safest way to convert this is by removing duplicates in a subquery:
select count(1), ne.equipment_name
from alarms.new_alarms ne left outer join
(select distinct eqp
from ne_db.ne_list
) eqp
on ne.equipment_name = eqp.eqp
Group by ne.equipment_name
You could use a left join:
select ne.equipment_name
, count(na.equipment_name)
from ne_db.ne_list ne
left join
alarms.new_alarms na
on ne.eqp = na.equipment_name
group by
ne.equipment_name
This should work too
(select equipment_name, count(*) as cnt from alarms.new_alarms
where equipment_name in (
select eqp from ne_db.ne_list
) group by equipment_name
)union(
select equipment_name, 0 as cnt from alarms.new_alarms
where equipment_name not in (
select eqp from ne_db.ne_list
) group by equipment_name
) order by equipment_name

use field in sql join where clause

I am trying to write a crystal report using a sql statement because it runs much much faster. But I am having trouble with some of the linkings. I need to use the result of a link for criteria in subsequent links.
Ok, here is a sample of what my statement looks like:
(The lines marked with ** are the lines in question)
SELECT
Part.PartNum,
Cust.CustNum,
Cust.CustID,
YTD.Qty
FROM
(
SELECT
Pub.Part.PartNum,
Pub.Part.UserChar1 AS CustID
FROM
Pub.Part
) AS Part
LEFT OUTER JOIN (
SELECT
Pub.Customer.CustID,
Pub.Customer.CustNum,
Pub.Customer.Name
FROM
Pub.Customer
WHERE
Pub.Customer.CustID = '1038'
) AS Cust
ON Part.CustID = Cust.CustID
LEFT OUTER JOIN (
SELECT
Pub.OrderDtl.PartNum,
Sum(Pub.OrderDtl.OrderQty) AS Qty
FROM
Pub.OrderHed JOIN Pub.OrderDtl ON
Pub.OrderHed.OrderNum = Pub.OrderDtl.OrderNum
WHERE
**Pub.OrderHed.CustNum = Cust.CustNum AND**
**Pub.OrderDtl.PartNum = Part.PartNum AND**
YEAR(Pub.OrderHed.OrderDate)=YEAR(CURDATE())
GROUP BY
Pub.OrderDtl.PartNum
) AS YTD ON Part.PartNum = YTD.PartNum
Now, I get an error that says:
Part.PartNum cannot be found or is not specified for the query.
I get the same error for Cust.CustNum. Will you help me figure out what I am doing wrong? Thanks!
The problem is that you are using one of the aliases, inside of a sub-query which you cannot do. You will have to do something similar to this:
SELECT Part.PartNum,
Cust.CustNum,
Cust.CustID,
YTD.Qty
FROM
(
SELECT Pub.Part.PartNum,
Pub.Part.UserChar1 AS CustID
FROM Pub.Part
) AS Part
LEFT OUTER JOIN
(
SELECT Pub.Customer.CustID,
Pub.Customer.CustNum,
Pub.Customer.Name
FROM Pub.Customer
WHERE Pub.Customer.CustID = '1038'
) AS Cust
ON Part.CustID = Cust.CustID
LEFT OUTER JOIN
(
SELECT Pub.OrderDtl.PartNum,
Sum(Pub.OrderDtl.OrderQty) AS Qty,
Pub.OrderHed.CustNum
FROM Pub.OrderHed
JOIN Pub.OrderDtl
ON Pub.OrderHed.OrderNum = Pub.OrderDtl.OrderNum
WHERE YEAR(Pub.OrderHed.OrderDate)=YEAR(CURDATE())
GROUP BY Pub.OrderDtl.PartNum, Pub.OrderHed.CustNum
) AS YTD
ON Part.PartNum = YTD.PartNum
AND Cust.CustNum = YTD.CustNum
Looking at your query more, you can actually get rid of two of the subqueries:
SELECT Part.PartNum,
Cust.CustNum,
Cust.CustID,
YTD.Qty
FROM Pub.Part Part
LEFT OUTER JOIN Pub.Customer Cust
ON Part.CustID = Cust.CustID
AND Cust.CustID = '1038'
LEFT OUTER JOIN
(
SELECT d.PartNum,
Sum(d.OrderQty) AS Qty,
h.CustNum
FROM Pub.OrderHed h
JOIN Pub.OrderDtl d
ON h.OrderNum = d.OrderNum
WHERE YEAR(h.OrderDate)=YEAR(CURDATE())
GROUP BY d.PartNum
) AS YTD
ON Part.PartNum = YTD.PartNum
AND Cust.CustNum = YTD.CustNum
This is because you can't access a parent sub-query (cust, part) within another sub-query (YTD)
However, the solution is easy in your case, filter in the ON clause instead:
SELECT
Part.PartNum,
Cust.CustNum,
Cust.CustID,
YTD.Qty
FROM
(
SELECT
Pub.Part.PartNum,
Pub.Part.UserChar1 AS CustID
FROM
Pub.Part
) AS Part
LEFT OUTER JOIN (
SELECT
Pub.Customer.CustID,
Pub.Customer.CustNum,
Pub.Customer.Name
FROM
Pub.Customer
WHERE
Pub.Customer.CustID = '1038'
) AS Cust
ON Part.CustID = Cust.CustID
LEFT OUTER JOIN (
SELECT
Pub.OrderDtl.PartNum,
Sum(Pub.OrderDtl.OrderQty) AS Qty,
Pub.OrderHed.CustNum
FROM
Pub.OrderHed JOIN Pub.OrderDtl ON
Pub.OrderHed.OrderNum = Pub.OrderDtl.OrderNum
WHERE
YEAR(Pub.OrderHed.OrderDate)=YEAR(CURDATE())
GROUP BY
Pub.OrderDtl.PartNum
) AS YTD ON Part.PartNum = YTD.PartNum AND Cust.CustNum = YTD.CustNum

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

Problem joining tables in SQL

SELECT MID, FAD.FirstOpenedDate ,LCD.LastCloseDate
FROM mwMaster.dbo.Merchant M
JOIN (
SELECT MerchID, MIN(moddate) AS FirstOpenedDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) FAD ON FAD.MerchID = M.MerchID
LEFT JOIN (
SELECT MerchID, MAX(moddate) AS LastCloseDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) LCD ON LCD.MerchID = M.MerchID
JOIN (
SELECT merchid ,avg(Transactions) ,avg(Profit)
FROM mwMaster.dbo.ResidualSummary RS
WHERE RS.Date_Processed < LCD.LastCloseDate
GROUP BY Merchid
) R ON R.MerchID = M.MerchID
I am having trouble performing the following join. I have run into this problem before and used temp tables but would like to find out what I am doing wrong. Basically the line that is not working is the 3rd to last. The "< LCD.LastClostDate" says that it cannot be bound. Is it possible to use the value from LCD which I created in a nested query above (in that query I used the M table in a similar way but I didnt run into any issue)? I am thinking becasue the LCD table is dynamically created here it cannot be used in the nested query but this is just my guess.
Any ideas?
On a side note I have also seen people using a CROSS and OVER. Not to farmiliar with how this works but may be applicable here?
I think though haven't tested you can just change your JOIN to a CROSS APPLY in SQL 2005+
SELECT MID, FAD.FirstOpenedDate ,LCD.LastCloseDate
FROM mwMaster.dbo.Merchant M
JOIN (
SELECT MerchID, MIN(moddate) AS FirstOpenedDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) FAD ON FAD.MerchID = M.MerchID
LEFT JOIN (
SELECT MerchID, MAX(moddate) AS LastCloseDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) LCD ON LCD.MerchID = M.MerchID
CROSS APPLY(
SELECT merchid ,avg(Transactions) ,avg(Profit)
FROM mwMaster.dbo.ResidualSummary RS
WHERE RS.Date_Processed < LCD.LastCloseDate
GROUP BY Merchid
) R ON R.MerchID = M.MerchID
But it might be easier to use CTEs
WITH LCD AS (SELECT MerchID, MAX(moddate) AS LastCloseDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID),
R AS (
SELECT merchid ,avg(Transactions) ,avg(Profit)
FROM mwMaster.dbo.ResidualSummary RS
INNER JOIN LCD on
LCD.MERCHID = RS.MERCHID
WHERE RS.Date_Processed < LCD.LastCloseDate
GROUP BY Merchid
)
SELECT MID, FAD.FirstOpenedDate ,LCD.LastCloseDate
FROM mwMaster.dbo.Merchant M
JOIN (
SELECT MerchID, MIN(moddate) AS FirstOpenedDate
FROM mwMaster.dbo.MerchantStatusHistory
GROUP BY MerchID
) FAD ON FAD.MerchID = M.MerchID
LEFT JOIN LCD ON LCD.MerchID = M.MerchID
LEFT JOIN R ON R.MerchID = M.MerchID
I can't really test this without your data, but here's one way you could do it:
SELECT MID,
MIN(moddate) OVER (PARTITION BY MerchID) as FirstOpenedDate,
MAX(moddate) OVER (PARTITION BY MerchID) as LastCloseDate
FROM mwMaster.dbo.Merchant
HAVING DateProcessed < LastCloseDate