T-SQL Query not returning proper results - tsql

I have a query that I want to capture Sales for Parts. I am expecting to get the full results from the Parts table and if there are no Sales for that Part in the timeframe, I want to see a 0 in the Sales column. I am not seeing that. I am just getting the Parts that had Sales.
SELECT
Part,
Sum(Sales)
FROM
dbo.Parts
LEFT OUTER JOIN
dbo.SalesData ON Part = Part
WHERE
SalesDate > '2011-12-31'
GROUP BY
Part
ORDER BY
Part
What am I doing wrong?

I believe this is because your WHERE clause is removing all the parts that don't have sales because they won't have a SalesDate.
Try:-
SELECT
Part,
Sum(Sales)
FROM
dbo.Parts
LEFT OUTER JOIN
dbo.SalesData ON Part = Part
AND SalesDate > '2011-12-31'
GROUP BY
Part
ORDER BY
Part

Related

PostgreSQL RANK() function over aggregated column

I'm constructing quite complex query, where I try to load users with their aggregated points altogether with their rank. I found the RANK() function that could help me to achieve this but can't get it working.
Here's the query that is working without RANK:
SELECT users.*, SUM(received_points.count) AS pts
FROM users
LEFT JOIN received_points ON received_points.user_id = users.id AND ...other joining conditions...
GROUP BY users.id
ORDER BY pts DESC NULLS LAST
Now I would like to select also the rank - but this way using RANK function it's not working:
SELECT users.*, SUM(received_points.count) AS pts,
RANK() OVER (ORDER BY pts DESC NULLS LAST) AS position
FROM users
LEFT JOIN received_points ON received_points.user_id = users.id AND ...other joining conditions...
GROUP BY users.id
ORDER BY pts DESC NULLS LAST
It tells: PG::UndefinedColumn: ERROR: column "pts" does not exist
I guess I get whole concept of window functions wrong. How can I select the rank of user sorted by aggregated value like pts in example above?
I know I can assign ranks manually afterwards but what if I want to also filter the rows according to users.name in query and still get user's rank in general (not-filtered) leaderboard...? Dont know if I'm clear...
As Marth suggested in his comment:
You can't use pts here as the alias doesn't exist yet (you can't reference an alias in the same SELECT it's defined). RANK() OVER (ORDER BY SUM(received_points.count) DESC NULLS LAST) should work fine.

Show field in MS Access query without including it in the group by clause

I'm working on a query that will eventually be used as the record source for a report.
I have a customers and orders table. I want to show customer_id, order_id, and order_date in a query, but I only want to show data associated with the earliest order date for each customer. Basically, I need to show the order_id field without including it in the group by clause. If I include it in the group by clause, I get a lot more records than I want. Based on my research, the code below will work in mysql, but not ms access.
Select customer.customer_id, order.order_id, min(order.order_dt)
From customer inner join order on customer.customer_id = order.customer_id
Group by customer.customer_id
I've tried grouping by order_id in a sub query and ordering by customer then date, then using the first function in the outer query. Unfortunately, the first function doesn't work as advertised.
Any help is greatly appreciated!
Does this work for you? It should bring up the earliest orders by order date for each customer. If there is more than one order on the earliest order date for a customer, all of those orders will be shown, though, so keep it in mind.
SELECT c.customer_id, o.order_id, o.order_dt
FROM customers AS c INNER JOIN (orders AS o INNER JOIN (SELECT customer_ID, MIN([order_dt]) AS MinOrder_dt FROM Orders GROUP BY customer_id) AS d ON (o.Customer_ID = d.customer_id) AND (o.[order_dt] = d.MinOrder_dt)) ON c.customer_id = o.customer_id;
I am deriving a table with just the customer_id and the min order_dt and joining customers and orders to that to only bring up the oldest orders.

How do I get matching rows Left Outer Join

This time I thought I had it figured out; but how can my addled brain explain this. No, for this I need the experts.
According to Jeff Atwood A Visual Explanation of SQL Joins Left outer join produces a complete set of records from Table A, with the matching records (where available) in Table B.
SELECT R.[Computer]
,L.[User]
,L.MaxDate
,R.[Notes]
,R.[ID]
From (
SELECT [User], max([StartDate]) as MaxDate
FROM <Table1>
Group by [User]
) As L
Left Outer Join <Table1> as R --Self join
on L.MaxDate = R.StartDate
MaxDate on the left always returns only one date for each User. This should be matched by exactly one matching row on the right. Or so I thought. I am getting multiple items for each date and user.
The purpose here is to return all the columns for each user using MaxDate to get the most recent date for each user. As the dates are unique, I should only get one row for each user, but instead I get several.
How do I limit the result set to the single matching row based on on L.MaxDate = R.StartDate ?
You get multiple matches from R if the same StartDate is found for multiple users. Add User to your join condition.

How to specify two expressions in the select list when the subquery is not introduced with EXISTS

I have a query that uses a subquery and I am having a problem returning the expected results. The error I receive is..."Only one expression can be specified in the select list when the subquery is not introduced with EXISTS." How can I rewrite this to work?
SELECT
a.Part,
b.Location,
b.LeadTime
FROM
dbo.Parts a
LEFT OUTER JOIN dbo.Vendor b ON b.Part = a.Part
WHERE
b.Location IN ('A','B','C')
AND
Date IN (SELECT Location, MAX(Date) FROM dbo.Vendor GROUP BY Location)
GROUP BY
a.Part,
b.Location,
b.LeadTime
ORDER BY
a.Part
I think something like this may be what you're looking for. You didn't say what version of SQL Server--this works in SQL 2005 and up:
SELECT
p.Part,
p.Location, -- from *p*, otherwise if no match we'll get a NULL
v.LeadTime
FROM
dbo.Parts p
OUTER APPLY (
SELECT TOP (1) * -- * here is okay because we specify columns outside
FROM dbo.Vendor v
WHERE p.Location = v.Location -- the correlation part
ORDER BY v.Date DESC
) v
WHERE
p.Location IN ('A','B','C')
ORDER BY
p.Part
;
Now, your query can be repaired as is by adding the "correlation" part to change your query into a correlated subquery as demonstrated in Kory's answer (you'd also remove the GROUP BY clause). However, that method still requires an additional and unnecessary join, hurting performance, plus it can only pull one column at a time. This method allows you to pull all the columns from the other table, and has no extra join.
Note: this gives logically the same results as Lamak's answer, however I prefer it for a few reasons:
When there is an index on the correlation columns (Location, here) this can be satisfied with seeks, but the Row_Number solution has to scan (I believe).
I prefer the way this expresses the intent of the query more directly and succinctly. In the Row_Number method, one must get out to the outer condition to see that we are only grabbing the rn = 1 values, then bop back into the CTE to see what that is.
Using CROSS APPLY or OUTER APPLY, all the other tables not involved in the single-inner-row-per-outer-row selection are outside where (to me) they belong. We aren't squishing concerns together. Using Row_Number feels a bit like throwing a DISTINCT on a query to fix duplication rather than dealing with the underlying issue. I guess this is basically the same issue as the previous point worded in a different way.
The moment you have TWO tables from which you wish to pull the most recent value, the Row_Number() solution blows up completely. With this syntax, you just easily add another APPLY clause, and it's crystal clear what you're doing. There is a way to use Row_Number for the multiple tables scenario by moving the other tables outside, but I still don't prefer that syntax.
Using this syntax allows you to perform additional joins based on whether the selected row exists or not (in the case that no matching row was found). In the Row_Number solution, you can only reasonably do that NOT NULL checking in the outer query--so you are forced to split up the query into multiple, separated parts (you don't want to be joining to values you will be discarding!).
P.S. I strongly encourage you to use aliases that hint at the table they represent. Please don't use a and b. I used p for Parts and v for Vendor--this helps you and others make sense of the query more quickly in the future.
If I understood you corrrectly, you want the rows with the max date for locations A, B and C. Now, assuming SQL Server 2005+, you can do this:
;WITH CTE AS
(
SELECT
a.Part,
b.Location,
b.LeadTime,
RN = ROW_NUMBER() OVER(PARTITION BY a.Part ORDER BY [Date] DESC)
FROM
dbo.Parts a
LEFT OUTER JOIN dbo.Vendor b ON b.Part = a.Part
WHERE
b.Location IN ('A','B','C')
)
SELECT Part,
Location,
LeadTime
FROM CTE
WHERE RN = 1
ORDER BY Part
In your subquery you need to correlate the Location and Part to the outer query.
Example:
Date = (SELECT MAX(Date)
FROM dbo.Vender v
WHERE v.Location = b.Location
AND v.Part = b.Part
)
So this will bring back one date for each location and part

SQLPLUS Table Trouble

I've been using SQLPLUS lately and one of my tasks was to display a set of values from two tables (stocks, orderitems). I have done this part, but I am stuck on the last part of the question which states: "including the stocks that no order has been placed on them so far".
Here is the statement:
`select Stocks.StockNo, Stocks.Description, OrderItems.QtyOrd
from Stocks INNER JOIN OrderItems
ON Stocks.StockNo = OrderItems.StockNo;`
and I have gotten the correct results for this part, but the second part is eluding me, as the curernt statement doesn't display the 0 values for QtyOrd.
Any help would be appreciated.
You likely want to use a LEFT OUTER JOIN otherwise the INNER JOIN will exclude Stocks which don't have any Orders. You might also consider grouping by Stock, in order to SUM the overall quantities for each stock?
SELECT Stocks.StockNo, Stocks.Description, SUM(OrderItems.QtyOrd) AS QtyOrd
FROM Stocks
LEFT OUTER JOIN OrderItems
ON Stocks.StockNo = OrderItems.StockNo
GROUP BY Stocks.StockNo, Stocks.Description;