Row_Number not avaiable in where but in order clause - sql-server

I am trying to use Row_Number, it works fine in the order by clause, but when using it in the where clause, i get invalid column, that dosnt make sense to me?
Anybody that can explain why that is? Thanks a bunch
SELECT col1,col2,
ROW_NUMBER() OVER(PARTITION BY col2 ORDER BY col2) as rownr
FROM table1 t1 WITH(NOLOCK)
JOIN table2 t2 WITH(NOLOCK) ON t2.id = t1.id
WHERE rownr > 1
ORDER BY rownr,unit

Logical processing of order by is after the select but where clause is processed before select thats why you get that error.
To do that you should make the query as sub-select and filter the records in outer query
SELECT col1,
col2
FROM (SELECT col1,
col2,
Row_number()OVER(PARTITION BY col2 ORDER BY col2) AS rownr,
unit
FROM table1 t1 WITH(NOLOCK)
JOIN table2 t2 WITH(NOLOCK)
ON t2.id = t1.id) a
WHERE rownr > 1
ORDER BY rownr, unit
check here for more info on Logical Processing Order of the SELECT statement

ROW_NUMBER() cannot be used with same query. Make ORDER BY in outer query.
EDIT : You cannot use rownr in the same query since you are computing ROW_NUMBER() in one place. You can access rownr in an outer query.
SELECT * FROM
(
SELECT col1,col2,unit,
ROW_NUMBER() OVER(PARTITION BY col2 ORDER BY col2) as rownr
FROM table1 t1 WITH(NOLOCK)
JOIN table2 t2 WITH(NOLOCK) ON t2.id = t1.id
)TAB
WHERE rownr > 1
ORDER BY rownr,unit

Related

Join a MS SQL Server WITH statement into a Select statement

I received this answer to this question MS SQL Server Last User Logged in Multiple Clients with Multiple Users and it works great.
;with cte as
(
select
client, myuser, lastlogin,
row_number() over (partition by client order by lastlogin desc) r#
from
#mytable
)
select *
from cte
where r# = 1
How do I get this joined to a regular Select statement that selects data from other tables also?
For example:
SELECT t1.id, t2.name
FROM table1 t1
JOIN table2 t2 ON (t2.id = t1.id)
WHERE t1.id = 1
There is no restriction, you can just join a result of a cte with other tables. Cte is a subquery, but it makes your code more readable.
;with cte as(
select client,myuser,lastlogin,row_number() over(partition by client order by lastlogin desc) r#
from #mytable
)
SELECT t1.id, t2.name
FROM table1 t1
JOIN table2 t2 ON (t2.id = t1.id)
JOIN cte t3 ON (...)
WHERE t1.id = 1
This is the same as a query with cte.
SELECT t1.id, t2.name
FROM table1 t1
JOIN table2 t2 ON (t2.id = t1.id)
JOIN ( select client,myuser,lastlogin,row_number() over(partition by client order by lastlogin desc) r#
from #mytable) t3 ON (...)
WHERE t1.id = 1

SubSelect Top 1 OR Left Join

I have a Select with sub selects using Top 1 and where clause.
I tried to optimize the select by doing a Left Join of the sub selects but the query time took longer. Is subselect better in this case? I couldnt post my whole select because it is too long and confidential but I will try to recreate the important part below:
Sub Select
SELECT
(select top 1 colId FROM table1 WHERE col1 = b.Id and col2 = 3 Order by 1) Id3,
(select top 1 colId FROM table1 WHERE col1 = b.Id and col2 = 5 Order by 1) Id5,
(select top 1 colId FROM table1 WHERE col1 = b.Id and col2 = 7 Order by 1) Id7
FROM table2 b
Trying it w/ Left Join
SELECT
t1.colid id3,
t2.colid id5,
t3.colid id7
FROM table2 b
LEFT JOIN (
select colId, col1 FROM table1 WHERE col2 = 3
) t1 ON t1.col1 = b.Id
LEFT JOIN (
select colId, col1 FROM table1 WHERE col2 = 5
) t2 ON t1.col1 = b.Id
LEFT JOIN (
select colId, col1 FROM table1 WHERE col2 = 7
) t3 ON t1.col1 = b.Id
Is there a better way to do this? and why is it the Left join takes longer query time?
You can use ROW_NUMBER:
;WITH cte AS
(
SELECT a.colId,
rn = ROWN_NUMBER() OVER (PARTITION BY a.col2 ORDER BY a.col1)
FROM table1 a
LEFT JOIN table2 b on a.col1 = b.id
WHERE a.col2 IN (3,5,7)
)
SELECT *
FROM cte
WHERE rn = 1
This will give you the first row for each col2 value and you can restrict the values you want to 3,5,7.

RANK() Over Partition BY not working

When I run the code below the ROWID is always 1.
I need to the ID to start at 1 for each item with the same Credit Value.
;WITH CTETotal AS (SELECT
TranRegion
,TranCustomer
,TranDocNo
,SUM(TranSale) 'CreditValue'
FROM dbo.Transactions
LEFT JOIN customers AS C
ON custregion = tranregion
AND custnumber = trancustomer
LEFT JOIN products AS P
ON prodcode = tranprodcode
GROUP BY
TranRegion
,TranCustomer
,TranDocNo)
SELECT
r.RegionDesc
,suppcodedesc
,t.tranreason as [Reason]
,t.trandocno as [Document Number]
,sum(tranqty) as Qty
,sum(tranmass) as Mass
,sum(transale) as Sale
,cte.CreditValue AS 'Credit Value'
,RANK() OVER (PARTITION BY cte.CreditValue ORDER BY cte.CreditValue)AS ROWID
FROM transactions t
LEFT JOIN dbo.Regions AS r
ON r.RegionCode = TranRegion
LEFT JOIN CTETotal AS cte
ON cte.TranRegion = t.TranRegion
AND cte.TranCustomer = t.TranCustomer
AND cte.TranDocNo = t.TranDocNo
GROUP BY
r.RegionDesc
,suppcodedesc
,t.tranreason
,t.trandocno
,cte.CreditValue
ORDER BY CreditValue ASC
EDIT
All the credit values with 400 must have the ROWID set to 1. And all the credit values with 200 must have the ROWID set to 2. And so on and so on.
Do you need something like this?
with cte (item,CreditValue)
as
(
select 'a',8 as CreditValue union all
select 'b',18 union all
select 'a',8 union all
select 'b',18 union all
select 'a',8
)
select CreditValue,dense_rank() OVER (ORDER BY item)AS ROWID from cte
Result
CreditValue ROWID
----------- --------------------
8 1
8 1
8 1
18 2
18 2
In your code replace
,RANK() OVER (PARTITION BY cte.CreditValue ORDER BY cte.CreditValue)AS ROWID
by
,DENSE_RANK() OVER (ORDER BY cte.CreditValue)AS ROWID
You just don't have to use PARTITION, just DENSE_RANK() OVER (ORDER BY cte.CreditValue)
I think the problem is with the RANK() OVER (PARTITION BY clause
you have to partition it by item not by CreditValue
Try this
RANK() OVER (PARTITION BY cte.CreditValue ORDER BY cte.RegionDesc)AS ROWID
Edit: The issue here isn't actually the nesting of the subquery, it's potentially based on partition by having columns that truly make each row unique (or 1)
Rather than ranking within your complex query like this
select
rank() over(partition by...),
*
from
data_source
join table1
join table2
join table3
join table4
order by
some_column
Try rank() or row_number() on the resulting data set, not within it.
For example, using the query above, remove rank() and implement it this way:
select
rank() over(partition by...),
results.*
from (
select
*
from
data_source
join table1
join table2
join table3
join table4
order by
some_column
) as results

Skip in SQL Server subquery

I want to use left outer join like this:
SELECT ...
FROM Table1
LEFT OUTER JOIN
(SELECT only e.g. 3rd record... , SomeField FROM Table2) tbl2
ON Table1.SomeField = tbl2.SomeField
How can I do that, if I need the subquery to select not just the 3rd record from Table2, but the 3rd record among the Table2 records that have SomeField = Table1.SomeField?
Thanks.
If this is sql server 2005 or newer, you might use row_number():
LEFT JOIN
(
select *
from
(
select *,
row_number() over (order by something) rn
from Table2
where Table2.Column = Table1.Column
) a
where a.rn = 3
) a
Unfortunately you need to nest it a level deeper because you cannot use row_number in a condition directly.
EDIT:
My bad - i didn't really notice the join part. If you want to join derived table, use this:
LEFT JOIN
(
select *,
row_number() over (partition by SomeField order by something) rn
from Table2
) tbl2
ON Table1.SomeField = tbl2.SomeField
AND tbl2.rn = 3
Note: you need ORDER BY in row_number() to keep things consistent.

Using where condition in sql query

I have an sql query like this
Select col1, (select abc from table2 where def=1) as col2
From Table1 inner join table3 on Table1.id = table3.id
Where col2 = 4
The problem is that the where condition doesn't work. I get an error saying
Invalid column name 'col2'
Kindly help me fix this sql query.
Thanks in advance
You can define it in a CROSS APPLY and then reference in the SELECT and WHERE
SELECT col1,
col2
FROM Table1
INNER JOIN table3
ON Table1.id = table3.id
CROSS APPLY (SELECT abc
FROM table2
WHERE def = 1) C(col2)
WHERE col2 = 4
Using a CTE (Common Table Expression):
WITH SubQuery AS (Col2) {
SELECT
ABC
FROM
table2
WHERE
def = 1
}
SELECT
T.Col1,
S.Col2
FROM
SubQuery S,
Table1 T
INNER JOIN table3 t3
ON T.id = t3.id
WHERE
S.Col2 = 4
Although I must say I agree with the first comment - this makes no sense since your subquery is not correlated (joined) to the rest of your query...

Resources