MSSQL - OVER, ROW_NUMBER() and ORDER BY error - sql

I'm trying to make a query that outputs a list with the company data and the number of Products and Discounts of each Company and order by product_count.
Also i need to limit the output to groups of 30 rows
SELECT * FROM (
SELECT *, (
SELECT COUNT(*) FROM Products WHERE Product_Comp_id = Comp_id
) as product_count, (
SELECT COUNT(*) FROM Discount WHERE Disc_Comp_id = Comp_id
) as discount_count , ROW_NUMBER() OVER (
ORDER BY product_count ASC
) AS RowNum FROM Company
) AS finalTable WHERE finalTable.RowNum BETWEEN 0 AND 30
But i get this error
Invalid column name 'product_count'.
Table Structure
Products
|-Product_id
|-Product_Name
|-Product_Description
|-Product_Comp_id
Discount
|-Disc_id
|-Disc_Name
|-Disc_Comp_id
|-Disc_Ammount
Company
|-Comp_id
|-Comp_Name
|-Comp_Address

You need an additional level of subquery to give you product_count.
SELECT * FROM (
SELECT * , ROW_NUMBER() OVER (ORDER BY product_count ASC) AS RowNum
FROM
(
SELECT *, (SELECT COUNT(*) FROM Products WHERE Product_Comp_id = Comp_id) as product_count,
(SELECT COUNT(*) FROM Discount WHERE Disc_Comp_id = Comp_id) as discount_count
FROM Company
) C
) AS finalTable WHERE finalTable.RowNum BETWEEN 0 AND 30

Related

How to get last date on max count transaction_id by branch in sql

I would like to get last date on max count txn_id base on branch_name.
This is the data
I want the result like this
here is my script. but I get only one row.
select max(date),
account,
branch_name,
province,
district
from
(select date,
account,
branch_name,
province,
district,
RANK() OVER (ODER BY txn_no desc) rnk
from
(select count(tr.txn_id) txn_no,
tr.date,
u.account,
b.branch_name,
b.province,
b.district
from transaction tr
inner join users u
on u.user_id = tr.user_id
inner join branch b
on b.user_id = u.user_id
where 1=1
and tr.date >= to_date('01/04/2021','dd/mm/yyyy') and tr.date < to_date('30/04/2021','dd/mm/yyyy')
group by tr.date,
u.account,
b.branch_name,
b.province,
b.district
))
where rnk = 1
group by tr.date,
u.account,
b.branch_name,
b.province,
b.district
WITH
cte1 AS ( SELECT *, COUNT(*) OVER (PARTITION user, branch) cnt
FROM source_table ),
cte2 AS ( SELECT *, RANK() OVER (PARTITION BY user ORDER BY cnt DESC) rnk
FROM cte1 )
SELECT *
FROM cte2
WHERE rnk = 1
If more than one branch have the same amount of rows then all of them will be returned. If only one of them must be returned in this case then according additional criteria must be used and ORDER BY expression clause in cte2 must be accordingly expanded. If any (random) must be returned in this case then RANK() in cte2 must be replaced with ROW_NUMBER().
I would do it with SELECT TOP 1 ... instead of RANK(), something like:
SELECT date,
txn_id,
account,
branch_name,
province,
district
FROM transaction t
WHERE branch_name = (
SELECT TOP 1 branch_name
FROM (
SELECT branch_name, count(*) as cnt
FROM transaction
WHERE account = t.account
GROUP BY branch_name
ORDER BY 2 DESC
) s
AND date = (
SELECT TOP 1 date
FROM (
SELECT date, count(*) as cnt
FROM transaction
WHERE account = t.account
AND branch_name = (
SELECT TOP 1 branch_name
FROM (
SELECT branch_name, count(*) as cnt
FROM transaction
WHERE account = t.account
GROUP BY branch_name
ORDER BY 2 DESC
) s2
GROUP BY branch_name
ORDER BY 2 DESC
) s2
)
If there are multiple transactions on the last date, they all are going to be returned though.
You want the most recent transaction from the account/branch with the most transactions. If so, you can do this as:
select t.*
from (select t.*,
max(cnt) over (partition by account) as max_cnt
from (select t.*,
count(*) over (partition by account, branch_name) as cnt,
row_number() over (partition by account, branch_name order by date desc) as seqnum
from t
) t
) t
where max_cnt = cnt and seqnum = 1;
Note: If there are multiple branches that have the same count, then they are all returned. Your question does not specify how to deal with such duplicates.

SQL Server different select statement on same table and different result

I have a products table that contains date-of-create, price, rate,....... and multiple columns I need one select statement that returns the 10 newest records, 10 lowest prices and the top rated products as separated result of each other something like
(select top 10 from products Order By Date ASC ) as newest_list
(select top 10 from products Order By price DESC ) as price_list
(select top 10 from products Order By Rate ASC ) as rateList_list
where ( newest_list price_list rateList_list) are different table result
Which better way to approach that result. Thanks.
You basically want union all . . . and a subquery:
select *
from ((select top 10 'newest' as which, p.*
from products p
Order By Date ASC
) union all
(select top 10 'priciest', p.*
from products p
Order By price DESC
) union all
(select top 10 'ratiest', p.*
from products p
Order By Rate ASC
)
) p
If you don't want duplicates, you can use union or window functions:
select p.*
from (select p.*,
row_number() over (order by date desc) as seqnum_d,
row_number() over (order by price desc) as seqnum_p,
row_number() over (order by rate asc) as seqnum_r
from p
) p
where seqnum_d <= 10 or seqnum_p <= 10 or seqnum_r <= 10;

Selecting City from Customer ID in SQL

Customer have ordered from different cities. Thus we have multiple cities against same customer_id. I want to display that city against customer id which has occurred maximum number of times , in case where customer has ordered same number of orders from multiple cities that city should be selected from where he has placed last order. I have tried something like
SELECT customer_id,delivery_city,COUNT(DISTINCT delivery_city)
FROM analytics.f_order
GROUP BY customer_id,delivery_city
HAVING COUNT(DISTINCT delivery_city) > 1
WITH cte as (
SELECT customer_id,
delivery_city,
COUNT(delivery_city) as city_count,
MAX(order_date) as last_order
FROM analytics.f_order
GROUP BY customer_id, delivery_city
), ranking as (
SELECT *, row_number() over (partition by customer_id
order by city_count DESC, last_order DESC) as rn
FROM cte
)
SELECT *
FROM ranking
WHERE rn = 1
select customer_id,
delivery_city,
amount
from
(
select t.*,
rank() over (partition by customer_id order by amount asc) as rank
from(
SELECT customer_id,
delivery_city,
COUNT(DISTINCT delivery_city) as amount
FROM analytics.f_order
GROUP BY customer_id,delivery_city
) t
)
where rank = 1

Get most expensive and cheapest items from two tables

I'm trying to get the most expensive and cheapest items from two different tables.
The output should be one row with the values for MostExpensiveItem, MostExpensivePrice, CheapestItem, CheapestPrice
I was able to get the price of the most expensive and cheapest items in the two tables with following query:
SELECT
MAX(ExtrasPrice) as MostExpensivePrice, MIN(ExtrasPrice) as CheapestPrice
FROM
(
SELECT ExtrasPrice FROM Extras
UNION ALL
SELECT ItemPrice FROM Items
) foo
How can I add the names of the items (ItemName, ExtrasName) to my output? Again, there should only be one row as the output.
Try this:
SELECT TOP 1 FIRST_VALUE(Price) OVER (ORDER BY Price) AS MinPrice,
FIRST_VALUE(Name) OVER (ORDER BY Price) AS MinName,
LAST_VALUE(Price) OVER (ORDER BY Price DESC) AS MaxPrice,
LAST_VALUE(Name) OVER (ORDER BY Price DESC) AS MaxName
FROM (
SELECT ExtrasName AS Name, ExtrasPrice AS Price FROM Extras
UNION ALL
SELECT ItemName As Name, ItemPrice AS Price FROM Items) u
SQL Fiddle Demo
TOP 1 with order by clause should work for you. Try this
SELECT *
FROM (SELECT TOP 1 ExtrasPrice,ExtrasName
FROM Extras ORDER BY ExtrasPrice Asc),
(SELECT TOP 1 ItemPrice,ItemName
FROM Items ORDER BY ItemPrice Desc)
Note: Comma can be replaced with CROSS JOIN
You can use row_number() for this. If you are satisfied with two rows:
SELECT item, price
FROM (SELECT foo.*, row_number() over (order by price) as seqnum_asc,
row_number() over (order by price) as seqnum_desc
FROM (SELECT item, ExtrasPrice as price FROM Extras
UNION ALL
SELECT item, ItemPrice FROM Items
) foo
) t
WHERE seqnum_asc = 1 or seqnum_desc = 1;
EDIT:
If you have an index on "price" in both tables, then the cheapest method is probably:
with exp as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price desc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price desc
)
),
cheap as (
(select top 1 item, ExtrasPrice as price
from Extras e
order by price asc
) union all
(select top 1 i.item, ItemPrice
from Items i
order by price asc
)
)
select top 1 *
from exp
order by price desc
union all
select top 1 *
from cheap
order by price asc;
If you want this in one row, you can replace the final query with:
select e.*, c.*
from (select top 1 *
from exp
order by price desc
) e cross join
(select top 1 *
from cheap
order by price asc
) c

Select records with a MAX(Date) but don't get distinct return

I am joining to tables and my code look like as follow:
SELECT P.*
FROM (
SELECT ABC.StockCode
,ABC.Supplier
,ABC.Price
,MAX(ABC.OrigReceiptDate) ReceiptDate
FROM (
SELECT GRND.StockCode
,GRND.Supplier
,OrigReceiptDate
,(GRND.OrigGrnValue / GRND.QtyReceived) AS Price
FROM [SysproCompanyR].[dbo].[GrnDetails] GRND
WHERE QtyReceived > 0
) ABC
GROUP BY ABC.StockCode
,ABC.Supplier
,ABC.Price
) P
WHERE P.StockCode IN (
SELECT StockCode
FROM [SysproCompanyR].[dbo].[InvMaster]
)
ORDER BY P.StockCode
,P.Price
The result set however is:
My problem here is i need to obtain the stock code and its price that was purchased latest...but I get duplicate values due to the prices that differs any idea to help out a blonde>
Try this:
WITH CTE
AS
(
SELECT
ABC.StockCode,
ABC.Supplier,
ABC.Price,
ABC.OrigReceiptDate AS ReceiptDate,
ROW_NUMBER() OVER(PARTITION BY ABC.Supplier
ORDER BY ABC.OrigReceiptDate DESC) AS RN
FROM
(
SELECT GRND.StockCode, GRND.Supplier,OrigReceiptDate,
(GRND.OrigGrnValue /GRND.QtyReceived) as Price
FROM [SysproCompanyR].[dbo].[GrnDetails] GRND
Where QtyReceived > 0
) AS ABC
INNER JOIN [SysproCompanyR].[dbo].[InvMaster] AS m
ON ABC.StockCode = m.StockCode
)
SELECT
StockCode,
Supplier,
Price,
ReceiptDate
FROM CTE
WHERE RN = 1;
This should give you the stock code and its price that was purchased latest for each supplier.
One more option with operator EXISTS and correlated subquery in WHERE clause
SELECT P.*
FROM (
SELECT GRND.StockCode, GRND.Supplier, GRND.OrigReceiptDate, (GRND.OrigGrnValue / GRND.QtyReceived) AS Price
FROM [SysproCompanyR].[dbo].[GrnDetails] AS GRND
WHERE QtyReceived > 0
) P
WHERE P.StockCode IN (
SELECT StockCode
FROM [SysproCompanyR].[dbo].[InvMaster]
)
AND EXISTS(
SELECT 1
FROM [SysproCompanyR].[dbo].[GrnDetails] AS GRND2
WHERE GRND.StockCode = GRND2.StockCode
AND GRND.Supplier = GRND2.Supplier
HAVING MAX(GRND2.OrigReceiptDate) = GRND.OrigReceiptDate
)
ORDER BY P.StockCode, P.Price
I believe this form of query is more understandable and can be easily maintained:
;WITH ABC AS (
SELECT
GRND.StockCode,
GRND.Supplier,
OrigReceiptDate,
Price = (GRND.OrigGrnValue / GRND.QtyReceived),
FROM [SysproCompanyR].[dbo].[GrnDetails] GRND
WHERE QtyReceived > 0
),
P AS (
SELECT
ABC1.StockCode,
lastOne.Supplier,
lastOne.Price,
ReceiptDate = lastOne.OrigReceiptDate
FROM
(SELECT DISTINCT StockCode FROM ABC) ABC1
CROSS APPLY(
SELECT TOP 1
Supplier,
Price,
OrigReceiptDate
FROM ABC
WHERE StockCode = ABC1.StockCode
ORDER BY OrigReceiptDate DESC
) lastOne
)
SELECT *
FROM P
WHERE
StockCode IN (
SELECT StockCode
FROM [SysproCompanyR].[dbo].[InvMaster])
ORDER BY
StockCode,
Price
I think you can do something like this:
SELECT a.*
FROM( SELECT StockCode,
Supplier,
OrigGrnValue/QtyReceived) AS Price,
OrigReceiptDate AS ReceiptDate
ROW_NUMBER() OVER (PARTITION BY StockCode, Supplier, OrigGrnValue/QtyReceived ORDER BY OrigReceiptDate DESC) as rown
FROM GrnDetails
WHERE QtyReceived > 0 ) a
WHERE a.rown = 1
AND a.StockCode EXISTS (SELECT StockCode
FROM [SysproCompanyR].[dbo].[InvMaster] m
WHERE m.StockCode = a.StockCode)
ORDER BY a.StockCode,
a.Price