Select Middle Rows in SQL Server - sql-server

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

Related

How to Remove Duplicate Statement

How to delete duplicate data row in SQL Server where there are not any unique value differences? I remain only one statement from my sales table (dbo.Sales)
ID DESCRIPTIONS QTY RATE AMOUNT
--------------------------------
1 APPLE 50 100 1000
1 APPLE 50 100 1000
1 APPLE 50 100 1000
1 APPLE 50 100 1000
We can try using a CTE here to arbitrarily delete all but one of the duplicates:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ID, DESCRIPTIONS, QTY, RATE, AMOUNT
ORDER BY (SELECT NULL)) rn
FROM yourTable
)
DELETE
FROM cte
WHERE rn > 1;
You can delete like following.
DELETE A
FROM (SELECT Row_number()
OVER (
partition BY id, descriptions, qty, rate, amount
ORDER BY (SELECT 1)) AS rn
FROM table1) A
WHERE a.rn > 1
If you want to use CTE, you can try like following.
;WITH cte
AS (SELECT Row_number()
OVER(
partition BY id, descriptions, qty, rate, amount
ORDER BY (SELECT 1)) RN
FROM table1)
DELETE FROM cte
WHERE rn > 1
you can use this:
select distinct * into temp from tableName
delete from tableName
insert into tableName
select * from temp
drop table temp
I suggest to add a column like rn and feed it by row_number() over (Partition by ID, DESCRIPTIONS ,QTY, RATE, AMOUNT order by Id)
Now delete the data having rn not equal to 1
after completion drop that column... this is a one time solution if it is frequent that add a unique key in your table

SQL Server: How to select fist 10 records from table without using TOP keyword

There is a table which contains 50 records. I want to select first 10 records without using TOP keyword.
In SQL Server 2012+ you can use OFFSET ... FETCH
SELECT *
FROM YourTable
ORDER BY YourColumn ASC
OFFSET 0 ROWS
FETCH FIRST 10 ROWS ONLY
You can use ROW_NUMBER and Common Table Expression to query any range of data.
USE AdventureWorks2012;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS RowNumber
FROM Sales.SalesOrderHeader
)
SELECT SalesOrderID, OrderDate, RowNumber
FROM OrderedOrders
WHERE RowNumber <= 10 -- other conditions: RowNumber between 50 and 60
Refere ROW NUMBER Here
Although it's probably the same thing internally, you can use
set rowcount 10
and then run the query.
I guess you can try something like this:
SELECT t.Id, t.Name FROM Table t
WHERE 10 > (SELECT count(*) FROM Table t2 WHERE t.id > t2.id)
You can use ROW_NUMBER. Let's say your table contains columns ID and Name. In that case you can use such query:
SELECT t.Id, t.Name
FROM (
SELECT ID, Name,
ROW_NUMBER() OVER (ORDER BY Id) AS RowNumber
FROM TableName
) t
WHERE RowNumber <= 10

SQL Update with row_number()

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

Filter first then select page

How to first filter the result based on params then to apply where-between?
Some thing like
With Results as
(
Select colName,Title, Row_Number(Over...) as row from a table where colName=5
)
Select * from Results
where
row between #first and #last
But it does not works. I need to move my where colName=5 from with clause to outside then I got wrong data as It first get rows between #first n #last then search for colName=5.
Also I want count of Results.
Any idea?
You can use COUNT(*) OVER() to get the count of the unfiltered results
WITH cte as
(
select *,
ROW_NUMBER() over (order by name desc) AS RN,
count(*) over() AS [Count]
from master..spt_values
)
SELECT name, number,[Count]
FROM cte
WHERE RN BETWEEN 20 AND 24
Returns
name number Count
----------------------------------- ----------- -----------
VIEW 8278 2506
VIEW 8278 2506
view 2 2506
varchar 3 2506
varbinary 1 2506
This has performance implications though. You might want to just calculate the COUNT up front and cache it somewhere rather than recalculating it for every page request.
Your ROW_NUMBER syntax is incorrect. It should be this:
With Results as
(
SELECT colName, Title, ROW_NUMBER() OVER (ORDER BY ...) AS RN
FROM your_table
WHERE colName = 5
)
SELECT * FROM Results
WHERE rn BETWEEN #first AND #last
ORDER BY rn
See the documentation for more information.
I use approach very similar to Martin Smiths (currently selected answer) and at least in the tests I've made it gives better performance results.
; WITH cte as
(
select *,
ROW_NUMBER() over (order by name desc) AS RN
from master..spt_values
)
SELECT name, number, (SELECT COUNT(*) FROM cte) AS [Count]
FROM cte
WHERE RN BETWEEN 20 AND 24
Run this and his queries side by side and compare execution plans.

autonumber in select statement in SQL Server

I would like to create a select query statement with autonumber.. like..
select * from tbl1
will give me everything from table.
The result I'd like to get is..
1 data
2 data
3 data
So how can I do to get that number..??
like..
select (for autonumber), * from tbl1
the data in my table will repeated (no unique data)
Use ROW_NUMBER:
SELECT ROW_NUMBER() OVER (ORDER BY col1) AS rn, * FROM tbl1
To filter the results based on the row number use this:
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY col1) AS rn, * FROM tbl1
) T1
WHERE rn = 5
You may need to find the identity's offset e.g. last ID of second table:
DECLARE #lastAutoID int
SET #lastAutoID = abs(( Select max(convert(float,[ConsID]))
FROM [RXPIPEDB]...[consumption] ) )
Then use ROW_NUMBER():
#lastAutoID + ROW_NUMBER() OVER (ORDER BY oldICN_str)

Resources