Efficient way to write this query - sql

I am trying to order the records by 3 columns and then select a particular ID and the record before that plus the row after that. Here is my query:
;With Cte As
(
SELECT ROW_NUMBER() Over(Order By Book, PageINT, [IDAuto]) as RowNum, [IdAuto]
FROM CCWiseInstr2
)
Select * From Cte
Where RowNum = (Select RowNum From Cte
Where IdAuto = 211079)
UNION
Select * From Cte
Where RowNum = (Select RowNum - 1 From Cte
Where IdAuto = 211079)
UNION
Select * From Cte
Where RowNum = (Select RowNum + 1 From Cte
Where IdAuto = 211079)
What could the other efficient way to write this query. At the moment the query takes about 336 ms after creating all indexes which looks like a bit higher to me.
Here is the plan for the query:
http://gyazo.com/9a7f1c37d4433665d0949acf03c4561c
Any help is appreciated.

How about this query:
;With Cte As
(
SELECT ROW_NUMBER() Over(Order By Book, PageINT, [IDAuto]) as RowNum, [IdAuto]
FROM CCWiseInstr2
)
Select RowNum, IDAuto From Cte
Where RowNum IN (
Select RowNumber From
(
Select RowNum - 1 as RowNumPrev,
RowNum as RowNum,
RowNum + 1 as RowNumNext
From Cte
Where IdAuto = 211079
) vw unpivot (
RowNumber For
IdAuto IN (RowNumPrev, RowNum, RowNumNext )
) unpw )
Instead of UNION just use UNPIVOT which will convert your columns into rows which you could then use in IN. Let me know how it goes.

You can use the LEAD and LAG functions with SQL Server. Here's a great article on Simple Talk covering all of the options. (Code below is untested)
https://www.simple-talk.com/sql/t-sql-programming/sql-server-2012-window-function-basics/
SELECT
[IdAuto],
LAG([IDAuto], 1) OVER(Order By Book, PageINT, [IDAuto]) AS PreviousSale,
LEAD([IDAuto], 1) OVER(Order By Book, PageINT, [IDAuto]) AS NextSale
FROM
CCWiseInstr2
WHERE [IdAuto] = 211079;

Related

SQL command for getting a specific pattern

I have a table sample with column 'observations':
Please help with the SQL command to get the following 'cumulative multiplication' output:
2
6
30
300
One method is a recursive CTE:
with tt as (
select t.*, row_number() over (order by obs) as seqnum
from t
),
cte as (
select obs as prod, seqnum
from tt
where seqnum = 1
union all
select cte.prod * tt.obs, tt.seqnum
from cte join
tt
on tt.seqnum = cte.seqnum + 1
)
select *
from cte;
Another uses arithmetic to implement a "product" window function:
select t.*,
exp(sum(log(obs)) over (order by obs))
from t;
Here is a db<>fiddle.

Applying LIMIT and OFFSET to MS SQL server 2008 queries

I need to apply LIMIT and OFFSET to original query (without modifying it) in MSSQL server 2008.
Let's say the original query is:
SELECT * FROM energy_usage
(But it can be any arbitrary SELECT query)
That's what I came up with so far:
1. It does what I need, but the query generates extra column row_number which I don't need.
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM energy_usage
)
SELECT * FROM OrderedTable WHERE row_number BETWEEN 1 AND 10
2. This one doesn't work for some reason and returns the following error.
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (SELECT * FROM energy_usage) as real_sql) as subquery
WHERE row_number BETWEEN 1 AND 10
More common case is:
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (real sql query) as real_sql) as subquery
WHERE row_number BETWEEN {offset} + 1 AND {limit} + {offset}
Error:
The column prefix 'real_sql' does not match with a table name or alias
name used in the query.
Simply do not put it on SELECT list:
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT col1, col2, col3 FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
SELECT * is common anti-pattern and should be avoided anyway. Plus ORDER BY (SELECT 1) will not give you guarantee of stable sort between executions.
Second if you need only ten rows use:
SELECT TOP 10 *
FROM energy_usage
ORDER BY ...
Unfortunately you won't get something nice as Selecting all Columns Except One
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT * EXCEPT row_number FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
This would solve the problem.
DECLARE #offset INT = 1;
DECLARE #limit INT = 10;
WITH Filtered AS (
SELECT TOP (#offset + #limit) *
FROM energy_usage
ORDER BY 1 ASC
), Results AS (
SELECT TOP (#limit) *
FROM Filtered
ORDER BY 1 DESC
)
SELECT *
FROM Results
ORDER BY 1 ASC;

Oracle: select values only from row with min(id)

SELECT
ass.assessmentAmount -- want to fetch assessmentAmount of min(ass.assessmentId)
ass.assessmentId
FROM
--bunch of joins
WHERE
ass.assessmentId = (SELECT min(ass2.assessmentId) FROM Assessment ass2
--same bunch of joins
It looks very confusing because I have 6 joins with conditions and I don't want to repeat it two times. Is there another way of doing this?
Use the MIN( ass.assessmentId ) OVER () analytic function:
SELECT *
FROM (
SELECT ass.assessmentAmount,
ass.assessmentId,
MIN( ass.assessmentId ) OVER () AS min_assessmentId
FROM --bunch of joins
)
WHERE assessmentId = min_assessmentId;
You can also use RANK():
SELECT *
FROM (
SELECT ass.assessmentAmount,
ass.assessmentId,
RANK() OVER ( ORDER BY ass.assessmentId ) AS rnk
FROM --bunch of joins
)
WHERE rnk = 1;
If assessmentId is UNIQUE and can only have a single row as a minimum then you could replace RANK with ROW_NUMBER; however, you could also then get the desired result using the ROWNUM pseudocolumn:
SELECT *
FROM (
SELECT ass.assessmentAmount,
ass.assessmentId
FROM --bunch of joins
ORDER BY ass.assessmentId ASC
)
WHERE ROWNUM = 1;
Use a CTE with a row_number
with CTE as
(
select assessmentId,
assessmentAmount ,
row_number() over (order by assessmentid asc) as rn
from --bunch of joins
)
select *
from CTE
where rn = 1

How do I create a temp table using this snippet?

how do i produce this SQL snippet as a temp table so I can join some other stuff into it?
with MyCTE AS
(
select *, RANK() OVER (PARTITION BY workplace ORDER BY Total DESC) AS Rank
from [dbo].[OriginDestination]
)
select * from MyCTE where Rank <= 5
Like this:
with MyCTE AS
(
select *, RANK() OVER (PARTITION BY workplace ORDER BY Total DESC) AS Rank
from [dbo].[OriginDestination]
)
select *
into #yourTempTable
from MyCTE
where Rank <= 5

Can you select the Nth row in the dataset returned by SELECT statement?

In pseudo-code, what I mean is, can you do something like this (similar to TOP):
SELECT Row 2
*
FROM Table
WHERE Column1 = Condition
I wanted to do something for testing - I can't think of a real world scenario for this. Just curious if it's possible. I can't find anything on the Interwebz about it.
This would give you the 9th row:
select top 1 *
from (
select top 9 *
from MyTable
order by 1) as x
order by 1 desc
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY col2) rn
FROM mytable
WHERE col1 = condition
) q
WHERE rn = 4
WITH QUERY AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY Column1) AS RowNumber
FROM Table
)
SELECT * FROM QUERY WHERE ROWNUMBER = N