SQL: How to include SubQuery column result in WHERE clause? - sql

How do I include the result of a sub query into a WHERE clause?
For example, I have the following statement (cut down because the original is large):
SELECT o.ID AS OrderSpecsID, o.CustomerID, o.EstimateNo, o.OrderYear,
(SELECT COUNT(*)
FROM OrderSpecs AS os
WHERE (o.OrderID = OrderID)) AS AmendmentCount
FROM OrderSpecs AS o LEFT OUTER JOIN
Orders ON o.OrderID = Orders.ID
WHERE (o.CustomerID = 30)
I want to include the AmendmentCount field in my WHERE clause like so:
WHERE (o.CustomerID = 30) AND (AmendmentCount > 0)
However, if I set the above I get the following error:
Invalid column name 'AmendmentCount'
How can I make the AmendmentCount field available to my WHERE clause?
Many thanks,
Rob

Use a CTE or subquery:
with cte as (
SELECT o.ID AS OrderSpecsID, o.CustomerID, o.EstimateNo, o.OrderYear,
(SELECT COUNT(*)
FROM OrderSpecs AS os
WHERE (o.OrderID = OrderID)
) AS AmendmentCount
FROM OrderSpecs AS o LEFT OUTER JOIN
Orders
ON o.OrderID = Orders.ID
)
select *
from cte
where (CustomerID = 30) and (AmendmentCount > 0);
If your case, though, the better way to write the query is probably to use window functions:
select os.*
from (select os.ID AS OrderSpecsID, os.CustomerID, os.EstimateNo, os.OrderYear,
count(o.OrderId) over (partition by os.OrderId) as AmendmentCount
from OrderSpecs os left outer join
Orders o
on os.OrderID = o.ID
) os
where (CustomerID = 30) and (AmendmentCount > 0);
I am a little unclear if the filter on CustomerId should be in the subquery or outer query in this case. One or the other should work for what you are doing.

you can put your select with a join in a query
FROM OrderSpecs
join (SELECT COUNT(*), OrderID
FROM OrderSpecs AS os
WHERE (o.OrderID = OrderID)) AS Amendment on Amendment.OrderID = OrderSpecs.OrderID

Related

Duplicate records while retrieving using inner join in SQL Server

I am facing issues as I get records from SQL. The output of records was not right and numbers returned are much bigger than what I expected it to be.
Here is my query:
var query=
$#"select CAST(CustomerAssignedTaskExec.ScheduledDispatchedDateTime as DATE) as 'Date',
CustomerTaskDetails.CustomerTaskTypeId, COUNT(*) as 'Count' from CustomerAssignedTaskExec
inner join CustomerAssignedTask on CustomerAssignedTask.Id = CustomerAssignedTaskExec.CustomerAssignedTaskId
inner join CustomerTaskDetails on CustomerTaskDetails.Id = CustomerAssignedTaskExec.CustomerTaskDetailsId
inner join CustomerAssignedTaskItemStatus on CustomerAssignedTaskItemStatus.Id =
CustomerAssignedTaskExec.AssignedTaskItemStatusId
inner join customers on customers.CustomerId = CustomerAssignedTask.CustomerId
where Customers.StoreId = #storeId and CustomerAssignedTask.TaskStatusId = #runningTaskStatusId
group by CAST(CustomerAssignedTaskExec.ScheduledDispatchedDateTime AS DATE),
CustomerTaskTypeId, CustomerAssignedTaskItemStatus.Id";
This is my expected result :
Count CustomerTaskTypeId ScheduledDispatchedDateTime
852 7 2019-08-20
but what I get is :
Count CustomerTaskTypeId ScheduledDispatchedDateTime
4694 7 2019-08-20
What are the reasons why this is happening? Thank you for the help.
You may try this. You may use 1 instead of *, it will count only the distinct rows created by your group by clause.
var query= $#"select CAST(CustomerAssignedTaskExec.ScheduledDispatchedDateTime as DATE) as 'Date',
CustomerTaskDetails.CustomerTaskTypeId, COUNT(1) as 'Count'
from CustomerAssignedTaskExec
inner join CustomerAssignedTask on CustomerAssignedTask.Id = CustomerAssignedTaskExec.CustomerAssignedTaskId
inner join CustomerTaskDetails on CustomerTaskDetails.Id = CustomerAssignedTaskExec.CustomerTaskDetailsId
inner join CustomerAssignedTaskItemStatus on CustomerAssignedTaskItemStatus.Id = CustomerAssignedTaskExec.AssignedTaskItemStatusId
inner join customers on customers.CustomerId = CustomerAssignedTask.CustomerId
where Customers.StoreId = #storeId and CustomerAssignedTask.TaskStatusId = #runningTaskStatusId
group by CAST(CustomerAssignedTaskExec.ScheduledDispatchedDateTime AS DATE), CustomerTaskTypeId, CustomerAssignedTaskItemStatus.Id";
try remove your CustomerAssignedTaskItemStatus.Id and CustomerTaskTypeId in your grouping and give me the results.
select CAST(CustomerAssignedTaskExec.ScheduledDispatchedDateTime as DATE) as 'Date',
COUNT(1) as 'Count'
from CustomerAssignedTaskExec
inner join CustomerAssignedTask on CustomerAssignedTask.Id = CustomerAssignedTaskExec.CustomerAssignedTaskId
inner join CustomerTaskDetails on CustomerTaskDetails.Id = CustomerAssignedTaskExec.CustomerTaskDetailsId
inner join CustomerAssignedTaskItemStatus on CustomerAssignedTaskItemStatus.Id = CustomerAssignedTaskExec.AssignedTaskItemStatusId
inner join customers on customers.CustomerId = CustomerAssignedTask.CustomerId
where Customers.StoreId = #storeId and CustomerAssignedTask.TaskStatusId = #runningTaskStatusId
group by CAST(CustomerAssignedTaskExec.ScheduledDispatchedDateTime AS DATE)
Try to use count(distinct <value>) instead of count(*) maybe that could help as there might be other columns associated leading to redundant data

SQL - Tracking Monthly Sales

I am writing a query to summarize sales by month. My problem is that my query only returns records for months with sales. For example, I am looking over a 15 month range. But for one specific part in the example below only 3 of the 15 months had sales.
I'm hoping to have 15 records show up and the other ones have 0's for sales. The reason I am hoping for the additional records is I want to take the standard deviation of this, and dropping records impacts that calculation.
Sample Code:
SELECT I.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(*) as TotalDemand
FROM OrderDetails OD
INNER JOIN Orders O on O.Id = OD.OrderId
INNER JOIN Items I on I.Id = OD.ItemId
WHERE
O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
AND I.PartNumber = '5144831-2'
GROUP BY I.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
Sample Current Output:
Part # | Year | Month | Demand
5144831-2 2017 1 1
5144831-2 2017 2 3
5144831-2 2016 3 1
Desired Output:
I would want an additional row such as:
5144831-2 2016 11 0
To show there were no sales in Nov 2016.
I do have a temp table #_date_array2 with the possible months/years, I think I need help incorporating a LEFT JOIN.
If you want to use left join, you would not be able to use it directly with the inner join. You can do the inner join inside the parenthesis and then do the left join outside to avoid messing with the results of left join. Try this:
SELECT Z.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(Z.OrderId) as TotalDemand
FROM Orders O
LEFT JOIN
(
SELECT OrderId, PartNumber
FROM
OrderDetails OD
INNER JOIN Items I ON I.Id = OD.ItemId
AND I.PartNumber = '5144831-2'
) Z
ON O.Id = Z.OrderId
AND O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
GROUP BY Z.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
To get a count of 0 for months with no order, avoid using count(*) and use count(OrderId) as given above.
Note - You will have to make sure the Orders table has all months and years available i.e. if there is no CreateDate value of, say, November 2016 in the Orders table(left table in the join), the output will also not produce this month's entry.
Edit:
Can you try this:
SELECT Z.PartNumber as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
COUNT(O.OrderId) as TotalDemand
FROM Orders O
RIGHT JOIN
(
SELECT OrderId, PartNumber
FROM
OrderDetails OD
INNER JOIN Items I ON I.Id = OD.ItemId
AND I.PartNumber = '5144831-2'
) Z
ON O.Id = Z.OrderId
AND O.CreateDate >= '1-1-2016'
AND O.CreateDate <= '3-31-2017'
GROUP BY Z.PartNumber, YEAR(O.CreateDate) , MONTH(O.CreateDate);
Assuming you have sales of something in every month, the simplest solution is to switch to conditional aggregation:
SELECT '5144831-2' as PartNumber,
YEAR(O.CreateDate) as CreateDateYear,
MONTH(O.CreateDate) as CreateDateMonth,
SUM(CASE WHEN I.PartNumber = '5144831-2' THEN 1 ELSE 0 END) as TotalDemand
FROM OrderDetails OD INNER JOIN
Orders O
ON O.Id = OD.OrderId INNER JOIN
Items I
ON I.Id = OD.ItemId
WHERE O.CreateDate >= '2016-01-01' AND
O.CreateDate <= '2017-03-31'
GROUP BY YEAR(O.CreateDate) , MONTH(O.CreateDate);
Note: This is something of a hack for solving the problem. More robust solutions involve generating the dates and using LEFT JOIN (or similar functionality). However, this is often the fastest way to get the result.
based on all of your comments on other posts etc it seems like you have a table that has a date range you want and you want to be able to run the analysis for multiple/all of the part numbers. So the main issue is you will need a cartesian join between your date table and partnumbers that were sold during that time in order to accomplish you "0"s when not sold.
;WITH cteMaxMinDates AS (
SELECT
MinDate = MIN(DATEFROMPARTS(CreateDateYear,CreateDateMonth,1))
,MaxDate = MAX(DATEFROMPARTS(CreateDateYear,CreateDateMonth,1))
FROM
#_date_array2
)
;WITH cteOrderDetails AS (
SELECT
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
FROM
#_date_array2 d
INNER JOIN Orders o
ON d.CreateDateMonth = MONTH(o.CreateDate)
AND d.CreateDateYear = YEAR(o.CreateDate)
INNER JOIN OrderDetails od
ON o.Id = od.OrderId
INNER JOIN Items i
ON od.ItemId = i.Id
AND i.PartNumber = '5144831-2'
)
, cteDistinctParts AS (
SELECT DISTINCT PartNumber
FROM
cteOrderDetails
)
SELECT
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
,COUNT(od.PartNumber) as TotalDemand
FROM
#_date_array2 d
CROSS JOIN cteDistinctParts p
LEFT JOIN cteOrderDetails od
ON d.CreateDateYear = od.CreateDateYear
AND d.CreateDateMonth = od.CreateDateMonth
AND p.PartNumber = od.PartNumber
GROUP BY
d.CreateDateYear
,d.CreateDateMonth
,I.PartNumber
To get ALL part numbers simply remove AND i.PartNumber = '5144831-2' join condition.

Limiting result sets by future date - SQL

The Query below produces a record for each Entry in the SP_ScheduleEvent Table.
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
INNER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
INNER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
I want to limit the result set by the nearest future EventDateTime.
So per material name i would like to see one EventDateTime, which should be the nearest future date to the current time.
And lastly, a record may not exist in the SP_ScheduleEvent table for a particular materialname, in which case there should be null returned for the EventDateTime column
SQLFiddle
How would i go about doing this?
First, your LEFT JOIN is immaterial, because the subsequent joins make it an INNER JOIN. Either use LEFT JOIN throughout the FROM statement or switch to INNER JOIN.
I think you can use ROW_NUMBER():
SELECT t.*
FROM (SELECT m.MaterialId, m.MaterialName, m.MaterialTitle, se.EventDateTime,
ROW_NUMBER() over (PARTITION BY m.MaterialId OVER se.EventDateTime DESC) as seqnum
FROM GB_Material m INNER JOIN
SP_ScheduleEvent se
on se.MaterialName = m.MaterialName INNER JOIN
SP_Schedule s
on s.ScheduleID = se.ScheduleID INNER JOIN
GB_Channel c
on c.ChannelID = s.ChannelID
WHERE se.EventDateTime > getdate() AND
(LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%')
) t
WHERE seqnum = 1
ORDER BY se.EventDateTime;
Use the ROW_NUMBER() function:
WITH cte AS (
SELECT m.MaterialId, m.MaterialTitle, se.EventDateTime, c.ChannelName,
ROW_NUMBER() OVER (PARTITION BY m.MaterialId ORDER BY EventDateTime ASC) AS rn
FROM GB_Material m
LEFT OUTER JOIN SP_ScheduleEvent se on se.MaterialName = m.MaterialName
LEFT OUTER JOIN SP_Schedule s on s.ScheduleID = se.ScheduleID
LEFT OUTER JOIN GB_Channel c on c.ChannelID = s.ChannelID
WHERE LOWER(m.MaterialName) like '%foo%' OR LOWER(m.MaterialTitle) like '%foo%'
AND se.EventDateTime > GETDATE()
)
SELECT * FROM cte
WHERE rn=1

Selecting Orders with multiple line items but not when one or more line items contains a defined list

I am having trouble with a SQL Server query. I have a couple of tables involved [Order] (I know, not named well) and [Order Entry].
Order Entry is basically a "Line Item" on an Order (so there are one or more per Order). There are various columns in Order Entry, one of which is ItemID (there is only one ItemID per Order Entry). I want a query that returns all rows (Orders) that don't contain one or more Order Entry's with a list of ItemID's defined in a list.
Here is what I have so far:
SELECT DISTINCT
oe.OrderID, StoreID
FROM
OrderEntry oe
INNER JOIN
[order] o ON o.ID = oe.OrderID
AND o.StoreID = oe.StoreID
AND oe.ItemID NOT IN (60, 856, 857, 858, 902, 59, 240, 57, 217, 853, 855, 854, 41)
What I want to do seem similar to this (below) but I can't figure it out:
SELECT all orders with more than one item and check all items status
Help please! (much appreciated)
If I'm understanding you correctly, you want something like this. Starting your query with the Orders table and using a left join will ensure that you get the orders you're looking for. By left joining with a match on ItemID, you can then check for nulls in your where statement to find the orders that don't have those line items.
select distinct o.OrderID, o.StoreID
from Orders o
left join OrderEntry oe on oe.OrderID = o.ID and oe.StoreID = o.StoreID
and oe.ItemID in (60,856,857,858,902,59,240,57,217,853,855,854,41)
where oe.OrderID is null
So to break this down a bit:
"select distinct... from Orders" means "get me everything from orders"
"left join OrderEntry on..." means "get me all OrderEntry records that meet this criteria; if no records meet the criteria, nulls are OK"
"where oe.OrderID is null" means "I only want to see the items in Orders that had no match in the left join"
If we had used an inner join instead, we'd have lost that "nulls are OK" part, so the where clause wouldn't work.
SELECT DISTINCT oe.OrderID, StoreID
FROM OrderEntry oe
WHERE NOT EXISTS (SELECT *
FROM [order] o
WHERE o.ID = oe.OrderID AND
o.StoreID = oe.StoreID AND
oe.ItemID IN ( 60,856,857,858,902,59,240,57,217,853,855,854,41 ))
Is this what you were after?
Try this:
select OrderId, StoreId
from Order O
where o.orderId not in (select OrderId from
OrderEntry d where d.ItemId IN (10,6,7,5) )
Regards
SELECT oe.OrderID, StoreID
FROM OrderEntry oe
INNER JOIN [order] o
ON o.ID = oe.OrderID
AND o.StoreID = oe.StoreID
GROUP BY oe.OrderID, StoreID
AND SUM (CASE WHEN oe.ItemID NOT IN ( 60,856,857,858,902,59,240,57,217,853,855,854,41 )
THEN 1
ELSE 0
END) = 0
I was able to take a few pieces from several of you (Miguel Guzman - you provided the spark I needed to get this) and got it working. Here is my final Query:
SELECT o.ID, o.StoreID
FROM [Order] o
JOIN PSD_ServiceTicket st
ON o.ID = st.WorkOrderID
AND o.StoreID = st.StoreID
WHERE o.StoreID = 101
AND o.Time >= '10/1/2013'
AND o.Time <= '10/18/2013'
AND o.ID NOT IN (SELECT OrderID
FROM OrderEntry oe
INNER JOIN [order] o
ON o.ID = oe.OrderID
AND o.StoreID = oe.StoreID
WHERE oe.StoreID = 101
AND o.Time >= '10/1/2013'
AND o.Time <= '10/18/2013'
AND oe.ItemID IN ( 60,856,857,858,902,59,240,57,217,853,855,854,41 )
)
AND (st.ServiceTypeID = 1 OR st.ServiceTypeID = 4 )
Thanks to everyone!

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