This is SQL Server 2016. I have the following data in only one table:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+--------+-------------+-----------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 0
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 0
I want to group by custID, prodID and title and update isMasterTitle field to 1 for every max() titleCount per group.
So, I want the following:
custID | prodID | title | titleCount | isMasterTitle
--------+--------+----------+------------+---------------
266 | 191750 | prod01 | 1 | 0
266 | 191750 | prod02 | 4 | 0
266 | 191750 | prod03 | 25 | 1
300 | 20125 | prod04 | 3 | 0
300 | 20125 | prod05 | 15 | 1
I'm trying the following:
UPDATE [dbo].[_Variations]
SET isMasterTitle = 1
FROM [dbo].[_Variations] v1
INNER JOIN (SELECT custID, prodID, MAX(titleCount) AS mtitleCount
FROM [_Variations]
GROUP BY custID,prodID) as v2 ON v1.custID = v2.custID and v1.prodID = v2.prodID and v1.titleCount = v2.mtitleCount
try the following:
;with cte
as
(
select isMasterTitle, ROW_NUMBER() over (partition by custID, prodID order by titleCount desc) rn
from #t
)
update cte
set isMasterTitle = 1
where rn = 1
select * from #t
Your given code also works fine.
Please find the db<>fiddle here.
I would recommend leveraging a powerful feature of SQL Server called the updateable common-table-expression.
You can build a cte that uses window functions to identify which row should be updated, and then directly update it; there is no need to join again the original table in the outer query. This makes the query both shorter and more efficient:
with cte as (
select
isMaster,
row_number() over(partition by custID, prodID order by titleCount desc) rn
from [dbo].[_Variations]
)
update cte set isMaster = 1 where rn = 1
I'm having trouble figuring out how I can check if records on a table are in a specific order. The simplified table design is essentially this:
+------------+----------------+--------+
| ID (GUID) | StartDate | NumCol |
+------------+----------------+--------+
| CEE8C17... | 8/17/2019 3:11 | 22 |
| BB22001... | 8/17/2019 3:33 | 21 |
| 4D40B12... | 8/17/2019 3:47 | 21 |
| 3655125... | 8/17/2019 4:06 | 20 |
| 3456CD1... | 8/17/2019 4:22 | 20 |
| 38BAF92... | 8/17/2019 4:40 | 19 |
| E60CBE8... | 8/17/2019 5:09 | 19 |
| 5F2756B... | 8/17/2019 5:24 | 18 |
+------------+----------------+--------+
The ID column is a non-sequential GUID. The table is sorted by default on the StartDate when data is entered. However I am trying to flag instances where the NumCol values are out of descending order. The NumCol values can be identical on adjacent records, but ultimately they must be descending.
+--------+
| NumCol |
+--------+
| 22 |
| *20 | <- I want the ID where this occurs
| 21 |
| 20 |
| 20 |
| 19 |
| 19 |
| 18 |
+--------+
I've tried LEFT JOIN this table to itself, but can't seem to come up with an ON clause that gives the right results:
ON a.ID <> b.ID AND a.NumCol > b.NumCol
I also thought I could use OFFSET n ROWS to compare the default sorted table against one with an ORDER BY NumCol performed on it. I can't come up with anything that works.
I need a solution that will work for both SQL Server and SQL Compact.
With EXISTS:
select t.* from tablename t
where exists (
select 1 from tablename
where numcol > t.numcol and startdate > t.startdate
)
Or with row_number() window function:
select t.id, t.startdate, t.numcol
from (
select *,
row_number() over (order by startdate desc) rn1,
row_number() over (order by numcol) rn2
from tablename
) t
where rn1 > rn2
See the demo.
This might be easiest:
select * from T t1
where NumCol < (select max(NumCol) from T t2 where t2.StartDate > t1.StartDate);
The exists version is probably better to optimize though.
Using analytic functions you could try this approach which finds breaks in the monotonicity of consecutive rows. It might not return all the rows you're interested in seeing:
with data as (
select *, lag(NumCol) over (order by StartDate desc) as prevNumCol
from T
)
select * from data where prevNumCol > NumCol;
Here's a better solution that's probably not available in both of your environments:
with data as (
select *,
max(NumCol) over (
order by StartDate desc
rows between unbounded preceding and current row
) as prevMax
from T
)
select * from data where prevMax > NumCol;
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
I have the following table:
OrderID | OldOrderID | Action | EntryDate | Source
1 | NULL | Insert | 2016-01-12| A
1 | NULL | Remove | 2016-01-13| A
2 | NULL | Insert | 2016-01-12| B
3 | NULL | Insert | 2016-01-12| C
4 | 3 | Insert | 2016-01-13| C
4 | NULL | Remove | 2016-01-14| C
I want to query all orders that are currently active orders - they dont have the action remove. Currently I do it with this query :
WITH Active AS
(
SELECT *, rn = ROW_NUMBER()
OVER (PARTITION BY OrderID,Source ORDER BY EntryDate DESC)
FROM Orders
)
SELECT *
FROM Active WHERE [Action] <> 'Remove' AND rn = 1;
The problem is that some orders get child orders (OrderID 3 gets a child OrderID 4) and if a child ever gets the Action Remove the query should also ignore the parent, but with the current query it dosent.
In short the current query gets me this result:
OrderID | OldOrderID | Action | EntryDate | Source
2 | NULL | Insert | 2016-01-12| B
3 | NULL | Insert | 2016-01-12| C
But I need this result:
OrderID | OldOrderID | Action | EntryDate | Source
2 | NULL | Insert | 2016-01-12| B
Is it possible to fix the query to get a result like this?
Try this:
;WITH CTE AS (
SELECT OrderID, OldOrderID, Action, EntryDate, Source,
COUNT(CASE WHEN Action = 'Remove' THEN 1 END)
OVER (PARTITION BY OrderID) AS IsRemoved,
ROW_NUMBER() OVER (PARTITION BY OrderID ORDER BY EntryDate) AS rn
FROM Orders
)
SELECT c1.*
FROM CTE AS c1
LEFT JOIN CTE AS c2 ON c1.OrderID = c2.OldOrderID AND c2.IsRemoved >= 1
WHERE c1.rn = 1 AND c1.IsRemoved = 0 AND c2.IsRemoved IS NULL
The above query uses COUNT() OVER() in order to count the number of occurrences of Action = 'Remove' within each OrderID partition. Hence, a value of IsRemoved that is equal to or greater than 1 identifies a 'removed' order.
I also asked the question on dba stackexchange and got the following answer, which works well.
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
)