SELECT DISTINCT TOP 5 in SQL Server - sql

I can't get this sql query right...
I want the top 5 latest comments from tblComment. The problem is that I get more then one comment with the same ProductID. I don't want that.
SELECT DISTINCT TOP 5
tblProduct.ProductID,
tblProduct.ProductName,
tblComment.DateAdded
FROM
tblComment
INNER JOIN
tblProduct ON tblProduct.ProductID = tblComment.ProductID
ORDER BY
tblComment.DateAdded DESC
What am I doing wrong?

Assuming your comment table has an id field try this:
SELECT TOP 5
tblProduct.ProductID,
tblProduct.ProductName,
tblComment.DateAdded
FROM tblComment
JOIN tblProduct ON tblProduct.ProductID = tblComment.ProductID
JOIN (Select ProductID, max(id) as maxid From tblComment Group By ProductId) t on tblComment.id = t.maxid
ORDER BY tblComment.DateAdded DESC

You would have to sub select - use the following example to suit your needs.
SELECT TOP 5 tblProduct.ProductID,
tblProduct.ProductName,
tblComment.DateAdded
FROM tblComment INNER JOIN
tblProduct ON tblProduct.ProductID = tblComment.ProductID
and tblProduct.ProductID
IN (
SELECT tblProduct.ProductID
FROM tblComment
INNER JOIN tblProduct ON tblProduct.ProductID = tblComment.ProductID
GROUP BY tblProduct.ProductID
HAVING count( tblProduct.ProductID ) =1
)

Products ranked by time of latest comment.
This approach uses a CTE and and a rank function. This query is small but on larger queries these functions can make things more organized and readable.
with lastComment as (
select c.productID, max(DateAdded) DateAdded,
row_number() over(order by max(dateAdded)) rank
from tblComment c
group by c.productID
)
SELECT
tblProduct.ProductID,
tblProduct.ProductName,
tblComment.DateAdded
FROM
tblProduct
join lastComment ON tblProduct.ProductID = lastCommnet.ProductID
WHERE
lastComment.rank >= 5
ORDER BY lastComment.rank

Related

Volume of store sql

i want the top 10 stores with the highest volume(percentage) of sales and the 5 with the lowest volume(percentage) of sales in a single report. Here's what i have done until now
SELECT TOP 10 Stores.Store_ID,Suppliers$.NAME,SUM(Store.QTY*Product.PRICE) AS StoreSales
FROM Stores
INNER JOIN Invoices$ ON Stores.store_ID=Invoices.Store_ID
INNER JOIN InvDetails ON Invoices.INVOICE_ID=InvDetails.INVOICE_ID
INNER JOIN Products ON Products.PRODUCT_ID=InvDetails.PRODUCT_ID
GROUP BY Stores.Store_ID,Stores.NAME
ORDER BY StoresSales DESC
select TOP 10 ...
from ...
group by ...
order by StoresSales DESC
union
select TOP 5 ...
from ...
group by ...
order by StoresSales ASC
You can do this with a CTE by dividing the query in two, the first part extracts the highest and the second part the lowest
With c as(
SELECT TOP 10 Stores.Store_ID,Suppliers$.NAME,SUM(Store.QTY*Product.PRICE) AS StoreSales
,cat = 'Max'
FROM Stores
INNER JOIN Invoices$ ON Stores.store_ID=Invoices.Store_ID
INNER JOIN InvDetails ON Invoices.INVOICE_ID=InvDetails.INVOICE_ID
INNER JOIN Products ON Products.PRODUCT_ID=InvDetails.PRODUCT_ID
GROUP BY Stores.Store_ID,Stores.NAME
ORDER BY StoresSales DESC
),
d as (
SELECT TOP 5 Stores.Store_ID,Suppliers$.NAME,SUM(Store.QTY*Product.PRICE) AS StoreSales
,cat = 'Min'
FROM Stores
INNER JOIN Invoices$ ON Stores.store_ID=Invoices.Store_ID
INNER JOIN InvDetails ON Invoices.INVOICE_ID=InvDetails.INVOICE_ID
INNER JOIN Products ON Products.PRODUCT_ID=InvDetails.PRODUCT_ID
GROUP BY Stores.Store_ID,Stores.NAME
ORDER BY StoresSales asc
)
Select *
From c
Union all
Select *
From d
You can use ROW_NUMBER() :
SELECT Stores.Store_ID, Suppliers$.NAME, SUM(Store.QTY*Product.PRICE) AS StoreSales,
ROW_NUMBER() OVER (PARTITION BY Stores.Store_ID, Suppliers$.NAME ORDER BY SUM(Store.QTY*Product.PRICE) DESC) AS MAX_SEQ,
ROW_NUMBER() OVER (PARTITION BY Stores.Store_ID, Suppliers$.NAME ORDER BY SUM(Store.QTY*Product.PRICE)) AS MIN_SEQ
FROM Stores INNER JOIN
Invoices$
ON Stores.store_ID = Invoices.Store_ID INNER JOIN
InvDetails
ON Invoices.INVOICE_ID = InvDetails.INVOICE_ID INNER JOIN
Products
ON Products.PRODUCT_ID = InvDetails.PRODUCT_ID
GROUP BY Stores.Store_ID, Suppliers$.NAME;
By using this, you can filter out :
SELECT t.*
FROM ( <query here>
) t
WHERE (t.MAX_SEQ <= 10 OR MIN_SEQ <= 5);
You could union your query with basically the same query, but change it to top 5 and in ascending order:
SELECT TOP 10 Stores.Store_ID,Suppliers$.NAME,SUM(Store.QTY*Product.PRICE) AS
StoreSales
FROM Stores
INNER JOIN Invoices$ ON Stores.store_ID=Invoices.Store_ID
INNER JOIN InvDetails ON Invoices.INVOICE_ID=InvDetails.INVOICE_ID
INNER JOIN Products ON Products.PRODUCT_ID=InvDetails.PRODUCT_ID
GROUP BY Stores.Store_ID,Stores.NAME
ORDER BY StoresSales DESC
UNION
SELECT TOP 5 Stores.Store_ID,Suppliers$.NAME,SUM(Store.QTY*Product.PRICE) AS
StoreSales
FROM Stores
INNER JOIN Invoices$ ON Stores.store_ID=Invoices.Store_ID
INNER JOIN InvDetails ON Invoices.INVOICE_ID=InvDetails.INVOICE_ID
INNER JOIN Products ON Products.PRODUCT_ID=InvDetails.PRODUCT_ID
GROUP BY Stores.Store_ID,Stores.NAME
ORDER BY StoresSales ASC

How can I get sum of Items from one table to another in Sql

How can I get sum of items from one table to another in sql
use join and subquery
select p.*,stock from theProduct p
join ( select prodcode,sum(Qty) as stock from thePurchaseDetail group by prodcode) t
on p.prodcode=t.prodcode
You can use APPLY :
select p.*, pp.stock
from theProduct p cross apply
(select sum(pp.qty) as stock
from thePurchaseDetail pp
where pp.prodcode = p.prodcode
) pp;
You can also use window function with JOIN :
select p.*, sum(pp.qty) over (partition by p.prodcode) as stock
from theProduct p inner join
thePurchaseDetail pp
on pp.prodcode = p.prodcode;
You would left join in case there are missing prodcodes in tblPurchaseDetail table
select p.*
,t.stock
from tblProduct p
left join ( select prodcode
,sum(Qty) as stock
from tblPurchaseDetail
group by prodcode
) t
on p.prodcode=t.prodcode

Alternative SQL ANSI for TOP WITH TIES

I have two tables:
product
id_product
description
price
id_category
category
id_category
description
I would like to know the categories that have more products. For example, the category food has 10 products and the eletronics too. They are the same.
Now I'm using SQL Server and I'm using TOP WITH TIES.
SELECT TOP 1 WITH TIES p.id_category, COUNT(*) as amount FROM product p
JOIN category c ON p.id_category = c.id_category
GROUP BY p.id_category
ORDER BY amount
Is there another way to solve this with good performance?
I tried also with DENSE_RANK where the position is = 1.
It also works.
SELECT * FROM (
SELECT p.id_category, COUNT(*) as amount, DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) position FROM product p
JOIN category c ON p.id_category = c.id_category
GROUP BY p.id_category
) rnk
WHERE rnk.position = 1
But I want this solution in SQL ANSI.
I tried using MAX(COUNT(*)) but it doesn't work.
Is there a general solution? Is This solution better than using TOP WITH TIES?
Here is a third option for SQL Server:
WITH cte AS (
SELECT p.id_category, COUNT(*) AS cnt
FROM product p
INNER JOIN category c
ON p.id_category = c.id_category
GROUP BY p.id_category
)
SELECT *
FROM cte
WHERE cnt = (SELECT MAX(cnt) FROM cte);
If you also cannot rely on CTEs being available, you can easily enough just inline the CTE into the query. From a performance point of view, DENSE_RANK would probably outperform my answer.
With the CTE removed this becomes:
SELECT *
FROM
(
SELECT p.id_category, COUNT(*) AS cnt
FROM product p
INNER JOIN category c
ON p.id_category = c.id_category
GROUP BY p.id_category
)
WHERE cnt = (SELECT MAX(cnt) FROM (
SELECT p.id_category, COUNT(*) AS cnt
FROM product p
INNER JOIN category c
ON p.id_category = c.id_category
GROUP BY p.id_category
));
This query would even run on MySQL. As you can see, the query is ugly, which is one reason why things like CTE and analytic functions were introduced into the ANSI standard.

SQL: improving join efficiency

If I turn this sub-query which selects sales persons and their highest price paid for any item they sell:
select *,
(select top 1 highestProductPrice
from orders o
where o.salespersonid = s.id
order by highestProductPrice desc ) as highestProductPrice
from salespersons s
in to this join in order to improve efficiency:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders ) o on s.id = o.salespersonid
It still touches every order record (it enumerates the entire table before filtering by salespersonid it seems.) However you cannot do this:
select *, highestProductPrice
from salespersons s join (
select salespersonid, highestProductPrice, row_number(
partition by salespersonid
order by salespersonid, highestProductPrice) as rank
from orders
where orders.salepersonid = s.id) o on s.id = o.salespersonid
The where clause in the join causes a `multi-part identifier "s.id" could not be bound.
Is there any way to join the top 1 out of each order group with a join but without touching each record in orders?
Try
SELECT
S.*,
T.HighestProductPrice
FROM
SalesPersons S
CROSS APPLY
(
SELECT TOP 1 O.HighestProductPrice
FROM Orders O
WHERE O.SalesPersonid = S.Id
ORDER BY O.SalesPersonid, O.HighestProductPrice DESC
) T
would
select s.*, max(highestProductPrice)
from salespersons s
join orders o on o.salespersonid = s.id
group by s.*
or
select s.*, highestProductPrice
from salespersons s join (select salepersonid,
max(highestProductPrice) as highestProductPrice
from orders o) as o on o.salespersonid = s.id
work?

How to get last children records with parent record from database

I have database with two tables:
Customers (Id PK, LastName)
and
Orders (Id PK, CustomerId FK, ProductName, Price, etc.)
I want to retrieve only customer' last orders details together with customer name.
I use .NET L2SQL but I think it's SQL question more than LINQ question so I post here SQL query I tried:
SELECT [t0].[LastName], (
SELECT [t2].[ProductName]
FROM (
SELECT TOP (1) [t1].[ProductName]
FROM [Orders] AS [t1]
WHERE [t1].[CustomerId] = [t0].[Id]
ORDER BY [t1].[Id] DESC
) AS [t2]
) AS [ProductName], (
SELECT [t4].[Price]
FROM (
SELECT TOP (1) [t3].[Price]
FROM [Orders] AS [t3]
WHERE [t3].[CustomerId] = [t0].[Id]
ORDER BY [t3].[Id] DESC
) AS [t4]
) AS [Price]
FROM [Customers] AS [t0]
Problem is that Orders has more columns (30) and with each column the query gets bigger and slower because I need to add next subqueries.
Is there any better way?
In SQL Server 2005 and above:
SELECT *
FROM (
SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY c.id ORDER BY o.id DESC) rn
FROM customers c
LEFT JOIN
orders o
ON o.customerId = c.id
) q
WHERE rn = 1
or this:
SELECT *
FROM customers c
OUTER APPLY
(
SELECT TOP 1 *
FROM orders o
WHERE o.customerId = c.id
ORDER BY
o.id DESC
) o
In SQL Server 2000:
SELECT *
FROM customers с
LEFT JOIN
orders o
ON o.id =
(
SELECT TOP 1 id
FROM orders oi
WHERE oi.customerId = c.id
ORDER BY
oi.id DESC
)