I have a table with Names, Countries and Status. I want get total by grouping by Names and Status but get only Top 3 Countries.
My table:
+------+---------+--------+
| Name | Country | Status |
+------+---------+--------+
| ABC | US | Open |
| ABC | US | Closed |
| ABC | US | Open |
| ABC | Japan | Open |
| ABC | Japan | Closed |
| ABC | China | Open |
| ABC | China | Closed |
| ABC | Italy | Open |
| DEF | US | Open |
| DEF | US | Closed |
| DEF | Japan | Open |
| DEF | Japan | Closed |
| DEF | China | Open |
| DEF | China | Closed |
| DEF | China | Closed |
| DEF | Italy | Open |
+------+---------+--------+
Desired output:
+------+---------+--------+-------+
| Name | Country | Status | Total |
+------+---------+--------+-------+
| ABC | US | Open | 2 |
| ABC | US | Closed | 1 |
| ABC | Japan | Open | 1 |
| ABC | Japan | Closed | 1 |
| ABC | China | Open | 1 |
| ABC | China | Closed | 1 |
| DEF | US | Open | 1 |
| DEF | US | Closed | 1 |
| DEF | Japan | Open | 1 |
| DEF | Japan | Closed | 1 |
| DEF | China | Open | 1 |
| DEF | China | Closed | 2 |
+------+---------+--------+-------+
I tried the following query but it didn't give me result I am looking for.
Select rs.Name, rs.Country, rs.Status, Count(*) as total from (
SELECT Name, Country, Status, Rank()
over (Partition BY Name
ORDER BY Country DESC ) AS Rank
FROM table1 ) rs WHERE Rank <= 3
You can use the following query:
;With CTE AS (
SELECT Name, Country, Status,
COUNT(*) OVER (PARTITION BY Name, Country) AS cnt
FROM mytable
), CTE2 AS (
SELECT Name, Country, Status,
DENSE_RANK() OVER (PARTITION BY Name ORDER BY cnt DESC, Country) AS seq
FROM CTE
)
SELECT Name, Country, Status, COUNT(*) AS Total
FROM CTE2
WHERE seq <= 3
GROUP BY Name, Country, Status
ORDER BY Name, Country
In case of the ties, the query picks the Country having the 'smallest' name in comparison to the other countries.
Your original query was definitely in the right direction (I even used it to figure out what output you wanted). However, your desired output is the result of several aggregations, not just a single analytic function. In the query below I first aggregate to get totals, then use rank to retain the first 3 groups. In case of ties this query picks the country which comes alphabetically first.
SELECT t.Name,
t.Country,
t.Status,
t.Total
DENSE_RANK() OVER (PARTITION BY t.Name ORDER BY t.Total DESC, t.Country) AS rn
FROM
(
SELECT Name, Country, Status, COUNT(*) AS Total
FROM table1
GROUP BY Name, Country, Status
) t
WHERE rn <= 3
try this one..
Select rs.Name, rs.Country, rs.Status, Count(*) as total from rs(
SELECT Name, Country, Status, Count(status) from mytable
group by status order by Count(status) desc
) rs limit 3
How about:
select Top 3 with ties * FROM(
select Name, country, Status
, count(*) as total
, count(*) over (Partition BY Name, Country) as rank
from mytable
group by Name, Country, Status
) i
order by i.rank desc
Could you please try below SQL script
;with cte as (
select *, COUNT(*) over (partition by country) cnt
from table1
), t3 as (
select distinct top 3 country, cnt
from cte order by cnt desc
)
select distinct *
from cte
inner join t3 on cte.country = t3.country
Output is as follows
Use below query.
;WITH CTE
AS
(
SELECT NAME,COUNTRY,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY COUNT(COUNTRY) DESC) ROWNO FROM TBLCOUNTRY
GROUP BY NAME,COUNTRY
)
SELECT C.NAME,C.COUNTRY,C.STATUS,COUNT(C.STATUS) TOTAL FROM TBLCOUNTRY C INNER JOIN CTE ON
C.NAME=CTE.NAME AND C.COUNTRY=CTE.COUNTRY AND CTE.ROWNO<=3
GROUP BY C.NAME,C.COUNTRY,C.STATUS
ORDER BY NAME,COUNTRY DESC, STATUS DESC
Related
I'm facing some kind of problem. I have table "Prices" with columns - ProductId, ShopId, Date, Price.
Table contains history of prices for products in diffrent shops. Each product can be in diffrent shop with diffrent price and Date.
I want to get sum of the lastest prices in all shops for each product.
| ProductId | ShopId | Date | Price |
|:---------:|:------:|:----------:|:------:|
| 1 | 1 | 2020.11.10 | 100 |
| 1 | 2 | 2020.11.10 | 120 |
| 2 | 3 | 2020.11.10 | 200 |
| 3 | 3 | 2020.10.05 | 170 |
| 4 | 4 | 2020.11.10 | 200 |
| 4 | 4 | 2019.09.05 | 250 |
The output I want to get is (ShopId and Date can be included in output):
| ProductId | PriceSum |
|:---------:|:--------:|
| 1 | 220 |
| 2 | 200 |
| 3 | 170 |
| 4 | 200 |
I have following query:
SELECT ProductId, ShopId, MAX(Date) as MaxDate
FROM Prices
GROUP BY ShopId, ProductId
ORDER BY ProductId
I've found solution, not the fastest but working.
select st.ProductId, SUM(st.Price)
from Prices as p1
cross apply
(
select ProductId, ShopId, MAX(Date) as MaxDate
from Prices
group by ShopId, ProductId
) as p2
where p2.MaxDate = p1.Dt
and p2.Shopid = p1.ShopId
and p2.ProductId = p1.ProductId
group by p1.ProductId
order by p1.ProductId
In your case DENSE RANK window function can help you. If two or more rows have the same rank value in the same partition, each of those rows will receive the same rank.
WITH LatestPricesCTE AS
(
SELECT *, DENSE_RANK() OVER (PARTITION BY ProductID ORDER BY Date DESC) Rank
FROM Prices
)
SELECT ProductId, SUM(Price) PriceSum
FROM LatestPricesCTE
WHERE Rank = 1
GROUP BY ProductId
For more information:
https://learn.microsoft.com/en-us/sql/t-sql/functions/dense-rank-transact-sql?view=sql-server-ver15
Use window function to identity the latest dates and filter out older recs
;With dat
As (SELECT ProductId, ShopId, Date , Price
, row_number() over partition by prodictid, date order by date desc)r
FROM Prices)
Select Productid
, sum(price) Pricesum
From dat
Where rid=1
Group by productid;
I have following table:
ID | source | Name | Age | ... | ...
1 | SQL | John | 18 | ... | ...
2 | SAP | Mike | 21 | ... | ...
2 | SQL | Mike | 20 | ... | ...
3 | SAP | Jill | 25 | ... | ...
I want to have one record for each ID. The idea behind this is that if the ID comes only once (no matter the Source), that record will be taken. But, If there are 2 records for one ID, the one containing SQL as source will be the used record here.
So, In this case, the result will be:
ID | source | Name | Age | ... | ...
1 | SQL | John | 18 | ... | ...
2 | SQL | Mike | 20 | ... | ...
3 | SAP | Jill | 25 | ... | ...
I did this with a partition over (ordered by Source desc), but that wouldn't work well if a third source will be added one day.
Any other options/ideas?
The easiest approach(in my opinion) is using a CTE with a ranking function:
with cte as
(
select ID, source, Name, Age, ... ,
rn = row_number() over (partition by ID order by case when source = 'sql'
then 0 else 1 end asc)
from dbo.tablename
)
select ID, source, Name, Age, ...
from cte
where rn = 1
You can use ROW_NUMBER:
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER( PARTITION BY ID
ORDER BY CASE WHEN [Source] = 'SQL' THEN 1 ELSE 2 END)
FROM dbo.YourTable
)
SELECT *
FROM CTE
WHERE RN = 1;
You can use the WITH TIES clause and the window function Row_Number()
Select Top 1 With Ties *
From YourTable
Order By Row_Number() over (Partition By ID Order By Case When Source = 'SQL' Then 0 Else 1 End)
How about
SELECT *
FROM table
WHERE ID in (
SELECT ID FROM test
group by ID
having count(ID) = 1)
OR source = 'SQL'
Based on the product and product key, update the column ord_by. There should be only one min and max for a product and product_key .
E.g: Table
+-------------+---------+-------+--------+
| Product_key | product | price | ord_by |
+-------------+---------+-------+--------+
| 1 | ABC | 10 | |
| 1 | ABC | 10 | |
| 1 | ABC | 20 | |
| 1 | ABC | 100 | |
| 1 | ABC | 100 | |
| 2 | EFG | 20 | |
| 2 | EFG | 40 | |
| 3 | ABC | 100 | |
+-------------+---------+-------+--------+
Expected output:
+-------------+---------+-------+--------+
| Product_key | product | price | ord_by |
+-------------+---------+-------+--------+
| 1 | ABC | 10 | Min |
| 1 | ABC | 10 | Mid |
| 1 | ABC | 20 | Mid |
| 1 | ABC | 100 | Mid |
| 1 | ABC | 100 | Max |
| 2 | EFG | 20 | Min |
| 2 | EFG | 40 | Max |
| 3 | ABC | 100 | None |
+-------------+---------+-------+--------+
My try :
;WITH ord_cte
AS (
SELECT product
,product_key
,max(price) as max_price
,min(price) as min_price
FROM t_prod_ord
group by product,product_key
)
UPDATE t1
SET ord_by = case
when t2.max_price =t2.min_price then 'none'
when t2.max_price=t1.price then 'max'
when t2.min_price=t1.price then 'min'
else 'mid' end
FROM t_prod_ord t1
INNER JOIN ord_cte t2 ON t1.product_key = t2.product_key and t1.product=t2.product
using this query it is updating more than one max and min value for column ord_by.
Generate row number for each Product_key order by Price in both ASC and DESC order. Then use the row number in CASE statement to find the Min/Max values
Count() Over() aggregate window function will help you find the total count of each Product_key which we can use it for finding None
Here is one way
;WITH cte
AS (SELECT *,
Row_number()OVER(PARTITION BY Product_key ORDER BY price) AS Min_KEY,
Row_number()OVER(PARTITION BY Product_key ORDER BY price DESC) AS Max_KEY,
Count(1)OVER(partition BY Product_key) AS cnt
FROM Yourtable)
SELECT Product_key,
product,
price,
CASE
WHEN cnt = 1 THEN 'None'
WHEN Min_KEY = 1 THEN 'Min'
WHEN Max_Key = 1 THEN 'Max'
ELSE 'Mid'
END
FROM cte
Another way to do with out cte...
SELECT [Product_key],
[product],
[price],
CASE
WHEN Max(RN)
OVER(
PARTITION BY PRODUCT_KEY, PRODUCT
)=1 AND RN=1 THEN 'NONE'
WHEN Min(RN)
OVER(
PARTITION BY PRODUCT_KEY, PRODUCT
) = RN THEN 'MIN'
WHEN Max(RN)
OVER(
PARTITION BY PRODUCT_KEY, PRODUCT
) = RN THEN 'MAX'
ELSE 'MID'
END ORDER_BY
FROM (SELECT *,
Row_number()
OVER(
PARTITION BY PRODUCT_KEY, PRODUCT
ORDER BY PRICE) RN
FROM TABLE1)Z
Let's say I have a table named tableA having a data of
| col1 | col2 |
| 1 | 5 |
| 1 | 6 |
| 1 | 7 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
| 3 | 3 |
| 3 | 2 |
| 3 | 1 |
Then what I would like to get is the last 2 occurrences of each unique value of col1. The result would be
| col1 | col2 |
| 1 | 6 |
| 1 | 7 |
| 2 | 3 |
| 2 | 4 |
| 3 | 2 |
| 3 | 1 |
Is there a single query to get this result?
You can use ROW_NUMBER
WITH CTE AS(
SELECT *, rn = ROW_NUMBER() OVER(PARTITION BY col1 ORDER BY col2 DESC)
FROM tbl
)
SELECT
col1, col2
FROM CTE
WHERE rn <= 2
Just expanding on Felix's answer, assuming there's an ID column: http://www.sqlfiddle.com/#!3/04a5e/3/0
WITH CTE AS(
SELECT *, rn = ROW_NUMBER() OVER(PARTITION BY cola ORDER BY id DESC)
FROM ta
)
SELECT
cola, colb
FROM CTE
WHERE rn <= 2
order by id
Need to reorder by ID to keep the correct order, plus ordering in the row_number() by the ID because col2 isn't always incremental.
I have a problem that I could easily solve if I had window functions available in Sybase, but I dont:
Consider a table test:
+------------+----------------+-------------+
| Account_Id | Transaction_Id | CaptureDate |
+------------+----------------+-------------+
| 1 | 1 | 2014-01-01 |
| 1 | 2 | 2013-12-31 |
| 1 | 3 | 2015-07-20 |
| 2 | 1 | 2012-02-20 |
| 2 | 2 | 2010-01-10 |
| ... | ... | ... |
+------------+----------------+-------------+
I want to get a result set containing for each Account The most recent CaptureDate with the corresponding Transaction_Id. With the window function row_number this would be easy:
select Accounts_Id, CaptureDate, Transaction_Id from
(select
CallAccounts_Id,
CaptureDate,
Transaction_Id,
ROW_NUMBER() OVER(partition by Accounts_Id order by CaptureDate desc) row
from test) tbl
where tbl.row = 1
but my sybase version does not have this. Obviously, sth like
select max(Transaction_Id ), max(Transaction_Id ), Account_Id
from test
group by Account_Id
does not work because it does not always give me the correct Transaction_Id.
How can I do this then in Sybase and not make it terribly verbose?
Thanks!
Try below:
SELECT Account_Id, Transaction_Id, CaptureDate
FROM test a
WHERE CaptureDate = (
SELECT MAX(CaptureDate)
FROM test b
WHERE a.Account_Id = b.Account_Id
)
EDIT 1:
Duplicate CaptureDate was not in your example, so I did not take care of that scenario. Try below:
SELECT Account_Id, Transaction_Id, CaptureDate
FROM test a
WHERE CaptureDate = (
SELECT MAX(CaptureDate)
FROM test b
WHERE a.Account_Id = b.Account_Id
)
AND Transaction_Id =
(
SELECT MAX(Transaction_Id)
FROM test c
WHERE a.Account_Id = c.Account_Id
AND a.CaptureDate = c.CaptureDate
)