I know the title is vague but I'm not sure how to phrase it.
I have a simple table (see image below).
I need to write a SQL statement that will retrieve a single record representing each [UNIT_ID] and [ISACTIVE] where [ISACTIVE] is the MAX for that particular [UNIT_ID].
Ideally, the result should look like this (notice that UNIT_ID=2 does not have ISACTIVE=1):
Doing a simple [ISACTIVE] > 0 IS NOT what I'm looking for because then UNIT_ID=2 would never show up in the record.
Here's what I've tried (failed):
SELECT
[ID], [UNIT_ID], [REV], [ISACTIVE], [DATE_ENTERED]
FROM
TestTable
WHERE
([ISACTIVE] = (SELECT MAX([ISACTIVE])
FROM TestTable
WHERE ([UNIT_ID] = [UNIT_ID])));
-- This code only reveals two records (its missing record #4)
Any help on the matter is appreciated!
With NOT EXISTS:
SELECT t.[ID], t.[UNIT_ID], t.[REV], t.[ISACTIVE], t.[DATE_ENTERED]
FROM TestTable AS t
WHERE NOT EXISTS (
SELECT 1 FROM TestTable
WHERE [UNIT_ID] = t.[UNIT_ID] AND [ISACTIVE] > t.[ISACTIVE]
)
or with ROW_NUMBER():
SELECT t.[ID], t.[UNIT_ID], t.[REV], t.[ISACTIVE], t.[DATE_ENTERED]
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [UNIT_ID] ORDER BY [ISACTIVE] DESC) AS rn
FROM TestTable
) AS t
WHERE t.rn = 1
The problem with your code is the subquery doesn't use an alias for the TestTable. Should be
SELECT
[ID], [UNIT_ID], [REV], [ISACTIVE], [DATE_ENTERED]
FROM TestTable ta
WHERE (
[ISACTIVE]=
(SELECT MAX(t.[ISACTIVE]) FROM TestTable t WHERE (t.[UNIT_ID]=ta.[UNIT_ID])));
Like #forpas says, I think that the best option is this:
SELECT t.[ID], t.[UNIT_ID], t.[REV], t.[ISACTIVE], t.[DATE_ENTERED]
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY [UNIT_ID] ORDER BY [UNIT_ID, ISACTIVE] DESC) AS row_number
FROM TestTable
) AS t
WHERE t.row_number = 1
The only difference is the order by clause, that have to have those columns UNIT_ID, ISACTIVE.
Hope this can help you.
Related
Suppose you have a table with non-unique values such as this:
CREATE TABLE accounts ( fname VARCHAR(20), lname VARCHAR(20))
GO
INSERT accounts VALUES ('Fred', 'Flintstone')
INSERT accounts VALUES ('Fred', 'Flintstone')
INSERT accounts VALUES ('Fred', 'Flintstone')
SELECT * FROM accounts
GO
Now using a ROW_NUMBER function, you can get a unique incrementing row number.
select *, ROW_NUMBER() over(order by (select null)) as rn
from accounts
But how do we this without using a ROW_NUMBER function. I tried giving each row a unique ID using NEWID() and then counting the rows as given below but it did not work as it gives me a non-unique number which does not start with 1.
Note that I do not want to alter the table to add a new column.
;with cte as
(select *
from accounts as e
cross apply (select newid()) as a(id)
)
select *, (select count(*)+1 from cte as c1 where c.id > c1.id) as rn
from cte as c
order by rn
SQL Fiddle for toying around is http://sqlfiddle.com/#!18/c270f/3/0
The following demonstrates why your code fails, but does not provide an alternative to Row_Number().
A column, TopId, is added to the final select that should get the minimum value generated by NewId() and report it in every row. Instead, a new value is generated for each row.
-- Sample data.
declare #Samples as Table ( FName VarChar(20), LName VarChar(20) );
insert into #Samples ( FName, LName ) values
( 'Fred', 'Flintstone' ), ( 'Fred', 'Flintstone' ), ( 'Fred', 'Flintstone' );
select * from #Samples;
-- Cross apply NewId() in a CTE.
;with cte as
( select *
from #Samples as S
cross apply ( select NewId() ) as Ph( Id ) )
select *, ( select count(*) from cte as c1 where c1.Id >= c.Id ) as RN,
-- The following column should output the minimum Id value from the table for every row.
-- Instead, it generates a new unique identifier for each row.
( select top 1 id from cte order by id ) as TopId
from cte as c
order by RN;
The execution plan shows that the CTE is treated as a view that is being evaluated repeatedly, thus generating conflicting Id values.
How about this:
SELECT
src.*,
SUM(DummyVal) OVER(ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RowId
FROM (
SELECT a.*, 1 AS DummyVal
FROM MyTable a
) src
It's still a window function, though, not sure if that matters.
Fiddle me this
You can create an function yourself to compute the row_number,
In this example, I had to calculate an index for a lesson within a course.
Window Function version:
SELECT *, ROW_NUMBER() OVER(PARTITION BY courseId) AS row_num FROM lessons;
I created helper-function, to compute the row_number without window function:
DELIMITER $$
CREATE FUNCTION getRowNumber (lessonId int, courseId int)
RETURNS int
DETERMINISTIC
BEGIN
DECLARE count int;
select count(l2.id) into count from lessons l2 where l2.courseId=courseId
and l2.id<=lessonId;
RETURN count;
END$$
DELIMITER ;
so, the final query is:
SELECT l.*, getRowNumber(l.id,l.courseId) as row_num FROM lessons l;
got the same result as the first query!
MySQL:
SELECT #rownum := #rownum + 1 AS rank, a.*
FROM accounts a,(SELECT #rownum := 0) r;
In ORACLE it would simply be
SELECT ROWNUM, a.*
FROM accounts a;
Both without window
I have the following table (must shorter version than the real one),
and I want to all the rows with max _ values for each _ displayed.
How should I do this?
Table Now
Table I want to have
thanks a lot in advance!!
Using the dense_rank function and a derived table would be appropriate for this (please note I used underscores instead of spaces in the column names):
select group_type
,desk_number
,comments
from
(select *
,dense_rank() over(partition by group_type order by desk_number desc) dr
from mytable) t1
where t1.dr = 1
I made a rextester sample that you can try here
Let me know if you have any questions.
How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?
This answers your question quite well but I will convert it for your convenience <3
SELECT *
FROM table
INNER JOIN
(SELECT comments, MAX([desk number]) AS MaxDesk
FROM table
GROUP BY comments) groupedtable
ON table.[desk number]= groupedtable.[desk number]
AND table.comments= groupedtable.MaxDesk
try this :
WITH CTE
AS
(
SELECT
SeqNo = ROW_NUMBER() OVER(ORDER BY CAST(DeskNumber AS INT) DESC PARTITION BY GroupType),
GroupType,
DeskNumber,
[Comment]
FROM YourTable
)
SELECT
*
FROM CTE WHERE CTE.SeqNo = 1
I have a table where I want to select the last 10% of rows, offset by 10% (so I want to select the last 80-90% of the data).
I wrote the following query
SELECT TOP 10 PERCENT
[col1], [col2]
FROM [table]
ORDER BY [col1] DESC
OFFSET 10 ROWS
But I receive the following error:
Line 5: Incorrect syntax near 'OFFSET'.
What am I doing wrong? I am using Microsoft SQL Server 2012 which should be compatible with OFFSET
Try something like this....
SELECT TOP (50) PERCENT *
FROM (
SELECT TOP (20) PERCENT
[col1]
,[col2]
FROM [table]
ORDER BY [col1] DESC
)T
ORDER BY [col1] ASC
You can use a simple good old not in:
SELECT TOP 10 PERCENT [col1], [col2]
FROM [table]
WHERE [col1] NOT IN (
SELECT TOP 10 PERCENT [col1]
FROM [table]
ORDER BY [col1] DESC
)
ORDER BY [col1] DESC
For your error message, is your database set to backwards compatibility mode?
The offset expression only allows you to specify row numbers, not percentages. You can select the 80-90 percentile like:
select *
from (
select 100.0 * row_number() over (order by FirstName desc) /
count(*) over () as perc_pos
from YourTable
) as SubQueryAlias
where 80 <= perc_pos and perc_pos < 90
If your looking for a way to present, to a web page for example, blocks of data..
Try
WITH Ordered AS
(
SELECT *,
ROW_NUMBER() OVER (ORDER BY ServerName) AS 'RowNumber'
FROM systems
)
SELECT *
FROM Ordered
WHERE RowNumber BETWEEN 11 AND 20
With this code, I was able to offer the user the first 10, then then second block of 10 (11 - 20) and so one.
Now, a word of caution. If you data changes frequently, this may suffer as it will give you the first 10 rows (or rows 50 to 60) at the time the query is done.
So, if new data is being added, that throws off the list, be warned. If you looking at a list of computers, for example, and someone adds a new server named "AAA", and your looking at the middle of the list, what was item 50 in one query, may be item 49 in the second query. (I hope I didn't confuse that even more).
with this code, rownum get a proper rownum from a list of records available in table and pick a middle one record from them and display as follows:
SELECT * FROM
(SELECT E.*, ROWNUM RM FROM MYCODE E)
WHERE RM=(SELECT COUNT(*)/2 FROM MYCODE);
Required Output of middle record from table:
select top 1 *
from Employee
where empid in (
select top 50 percent empid
from employee
order by empid
)
order by empid desc
declare #middle1 as int
set #middle1 = ((select COUNT(*) from [table] )+1)/2
declare #middle2 as int
set #middle2 = ((select COUNT(*) from [table] ))/2
select * from
(select ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as R, * from [table] where (select COUNT(*) from [table] ) % 2 = 0) T2
where (T2.R - #middle2 = 0) or (T2.R- #middle1 = 0)
union
select * from
(select ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) as R, * from [table] where (select COUNT(*) from [table] ) % 2 != 0) T2
where T2.R - #middle1 = 0
I want to update my column CODE_DEST with an incremental number. I have:
CODE_DEST RS_NOM
null qsdf
null sdfqsdfqsdf
null qsdfqsdf
I would like to update it to be:
CODE_DEST RS_NOM
1 qsdf
2 sdfqsdfqsdf
3 qsdfqsdf
I have tried this code:
UPDATE DESTINATAIRE_TEMP
SET CODE_DEST = TheId
FROM (SELECT Row_Number() OVER (ORDER BY [RS_NOM]) AS TheId FROM DESTINATAIRE_TEMP)
This does not work because of the )
I have also tried:
WITH DESTINATAIRE_TEMP AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE DESTINATAIRE_TEMP SET CODE_DEST=RN
But this also does not work because of union.
How can I update a column using the ROW_NUMBER() function in SQL Server 2008 R2?
One more option
UPDATE x
SET x.CODE_DEST = x.New_CODE_DEST
FROM (
SELECT CODE_DEST, ROW_NUMBER() OVER (ORDER BY [RS_NOM]) AS New_CODE_DEST
FROM DESTINATAIRE_TEMP
) x
DECLARE #id INT
SET #id = 0
UPDATE DESTINATAIRE_TEMP
SET #id = CODE_DEST = #id + 1
GO
try this
http://www.mssqltips.com/sqlservertip/1467/populate-a-sql-server-column-with-a-sequential-number-not-using-an-identity/
With UpdateData As
(
SELECT RS_NOM,
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE DESTINATAIRE_TEMP SET CODE_DEST = RN
FROM DESTINATAIRE_TEMP
INNER JOIN UpdateData ON DESTINATAIRE_TEMP.RS_NOM = UpdateData.RS_NOM
Your second attempt failed primarily because you named the CTE same as the underlying table and made the CTE look as if it was a recursive CTE, because it essentially referenced itself. A recursive CTE must have a specific structure which requires the use of the UNION ALL set operator.
Instead, you could just have given the CTE a different name as well as added the target column to it:
With SomeName As
(
SELECT
CODE_DEST,
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE SomeName SET CODE_DEST=RN
This is a modified version of #Aleksandr Fedorenko's answer adding a WHERE clause:
UPDATE x
SET x.CODE_DEST = x.New_CODE_DEST
FROM (
SELECT CODE_DEST, ROW_NUMBER() OVER (ORDER BY [RS_NOM]) AS New_CODE_DEST
FROM DESTINATAIRE_TEMP
) x
WHERE x.CODE_DEST <> x.New_CODE_DEST AND x.CODE_DEST IS NOT NULL
By adding a WHERE clause I found the performance improved massively for subsequent updates. Sql Server seems to update the row even if the value already exists and it takes time to do so, so adding the where clause makes it just skip over rows where the value hasn't changed. I have to say I was astonished as to how fast it could run my query.
Disclaimer: I'm no DB expert, and I'm using PARTITION BY for my clause so it may not be exactly the same results for this query. For me the column in question is a customer's paid order, so the value generally doesn't change once it is set.
Also make sure you have indexes, especially if you have a WHERE clause on the SELECT statement. A filtered index worked great for me as I was filtering based on payment statuses.
My query using PARTITION by
UPDATE UpdateTarget
SET PaidOrderIndex = New_PaidOrderIndex
FROM
(
SELECT PaidOrderIndex, SimpleMembershipUserName, ROW_NUMBER() OVER(PARTITION BY SimpleMembershipUserName ORDER BY OrderId) AS New_PaidOrderIndex
FROM [Order]
WHERE PaymentStatusTypeId in (2,3,6) and SimpleMembershipUserName is not null
) AS UpdateTarget
WHERE UpdateTarget.PaidOrderIndex <> UpdateTarget.New_PaidOrderIndex AND UpdateTarget.PaidOrderIndex IS NOT NULL
-- test to 'break' some of the rows, and then run the UPDATE again
update [order] set PaidOrderIndex = 2 where PaidOrderIndex=3
The 'IS NOT NULL' part isn't required if the column isn't nullable.
When I say the performance increase was massive I mean it was essentially instantaneous when updating a small number of rows. With the right indexes I was able to achieve an update that took the same amount of time as the 'inner' query does by itself:
SELECT PaidOrderIndex, SimpleMembershipUserName, ROW_NUMBER() OVER(PARTITION BY SimpleMembershipUserName ORDER BY OrderId) AS New_PaidOrderIndex
FROM [Order]
WHERE PaymentStatusTypeId in (2,3,6) and SimpleMembershipUserName is not null
I did this for my situation and worked
WITH myUpdate (id, myRowNumber )
AS
(
SELECT id, ROW_NUMBER() over (order by ID) As myRowNumber
FROM AspNetUsers
WHERE UserType='Customer'
)
update AspNetUsers set EmployeeCode = FORMAT(myRowNumber,'00000#')
FROM myUpdate
left join AspNetUsers u on u.Id=myUpdate.id
Simple and easy way to update the cursor
UPDATE Cursor
SET Cursor.CODE = Cursor.New_CODE
FROM (
SELECT CODE, ROW_NUMBER() OVER (ORDER BY [CODE]) AS New_CODE
FROM Table Where CODE BETWEEN 1000 AND 1999
) Cursor
If table does not have relation, just copy all in new table with row number and remove old and rename new one with old one.
Select RowNum = ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) , * INTO cdm.dbo.SALES2018 from
(
select * from SALE2018) as SalesSource
In my case I added a new column and wanted to update it with the equevilat record number for the whole table
id name new_column (ORDER_NUM)
1 Ali null
2 Ahmad null
3 Mohammad null
4 Nour null
5 Hasan null
6 Omar null
I wrote this query to have the new column populated with the row number
UPDATE My_Table
SET My_Table.ORDER_NUM = SubQuery.rowNumber
FROM (
SELECT id ,ROW_NUMBER() OVER (ORDER BY [id]) AS rowNumber
FROM My_Table
) SubQuery
INNER JOIN My_Table ON
SubQuery.id = My_Table.id
after executing this query I had 1,2,3,... numbers in my new column
I update a temp table with the first occurrence of part where multiple parts can be associated with a sequence number. RowId=1 returns the first occurence which I join the tmp table and data using part and sequence number.
update #Tmp
set
#Tmp.Amount=#Amount
from
(SELECT Part, Row_Number() OVER (ORDER BY [Part]) AS RowId FROM #Tmp
where Sequence_Num=#Sequence_Num
)data
where data.Part=#Tmp.Part
and data.RowId=1
and #Tmp.Sequence_Num=#Sequence_Num
I don't have a running ID in order to do what "Basheer AL-MOMANI" suggested.
I did something like this: (joined my table on myself, just to get the Row Number)
update T1 set inID = T2.RN
from (select *, ROW_NUMBER() over (order by ID) RN from MyTable) T1
inner join (select *, ROW_NUMBER() over (order by ID) RN from MyTable) T2 on T2.RN = T1.RN
I want to update row data where the row_number of the column (p_id) is 1.. but this syntax is providing error:
update app1
set p_id = 1
where Row_Number() = 1 over(p_id)
You can't use ROW_NUMBER() directly - you need to e.g. use a CTE (Common Table Expression) for that:
;WITH DataToUpdate AS
(
SELECT
SomeID,
p_id,
ROW_NUMBER() OVER(ORDER BY .......) AS 'RowNum'
FROM
dbo.app1
)
UPDATE DataToUpdate
SET p_id = 1
WHERE
RowNum = 1
In order to use the ROW_NUMBER function, you also need at least an ORDER BY clause to define an order by which the rows are ordered.
From your question, it's not very clear what criteria (column) you want to order by to determine your ROW_NUMBER(), and it's also not clear what kind of column there is to uniquely identify a row (so that the UPDATE can be applied)
This will update only the first employee of that age. May be used as a lottery type logic
create table emp(name varchar(3),Age int, Salary int, IncentiveFlag bit)
insert into emp values('aaa',23,90000,0);
insert into emp values('bbb',22,50000,0);
insert into emp values('ccc',63,60000,0);
insert into emp values('ddd',53,50000,0);
insert into emp values('eee',23,80000,0);
insert into emp values('fff',53,50000,0);
insert into emp values('ggg',53,50000,0);
update A
set IncentiveFlag=1
from
(
Select row_number() over (partition by Age order by age ) AS SrNo,* from emp
)A
where A.SrNo=1
TO Delete duplicates ;WITH CTE(Name,Address1,Phone,RN)
AS
(
SELECT Name,Address1,Phone,
ROW_NUMBER() OVER(PARTITION BY Name ORDER BY Name) AS RN
)
DELETE FROM CTE WHERE RN > 1