SQL WITH Statement, Unknown Column in where clause - sql

I ve got the following query which is throwing the following error
Unkown Column 'RowNum'
WITH Employees AS
(
SELECT
(keyTblSp.RANK * 3) AS [Rank],
sp.*,
addr.Street,
addr.PostOfficeBox,
addr.StreetNumber
FROM Employee sp
INNER JOIN
FREETEXTTABLE(Employee, *, 'something', 1000) AS keyTblSp
ON sp.EmployeeId = keyTblSp.[KEY]
LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId
UNION ALL
SELECT
(keyTblAddr.RANK * 2) AS [Rank],
sp.*,
addr.Street,
addr.PostOfficeBox,
addr.StreetNumber
FROM Employee sp
LEFT OUTER JOIN [Address] addr ON addr.EmployeeId = sp.EmployeeId
INNER JOIN
FREETEXTTABLE([Address], *, 'something', 1000) AS keyTblAddr
ON addr.AddressId = keyTblAddr.[KEY]
)
SELECT ROW_NUMBER() OVER (ORDER BY [Rank] DESC) AS RowNum, *
FROM Employees
WHERE RowNum BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
ORDER BY Rank DESC

This is because aliases are not recognized in WHERE clauses. Instead, use the full query like this:
WHERE ROW_NUMBER() OVER (ORDER BY [Rank] DESC) BETWEEN (1 - 1) * 10 + 1 AND 1 * 10

Try wrpping up your query to get the name usable in the where clause
SELECT
*
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY [Rank] DESC) AS RowNum
, *
FROM
Employees) AS Results
WHERE
RowNum BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
ORDER BY
Rank

Your WHERE clause cannot refer to a window or aggregate function like ROW_NUMBER(). If you want to filter on the result of ROW_NUMBER(), you need to do so in the HAVING clause:
...
SELECT ROW_NUMBER() OVER (ORDER BY [Rank] DESC) AS RowNum, *
FROM Employees
HAVING RowNum BETWEEN (1 - 1) * 10 + 1 AND 1 * 10
ORDER BY Rank DESC

How about:
select top 10 *
from Employees
order by Rank Desc
Alternatively, does it work without the where rownum clause. (why is your between so tricky?).

Related

Random allocation of records between two tables

I want to assign the values from Table B to Table A, but so that each record in Table B occurs in the same number of repetitions.
Fiddle SQL
You can use window functions for this and mod arithmetic. For simple repetition:
with a as (
select a.*, rownum as seqnum
from a
),
b as (
select b.*, rownum as seqnum, count(*) over () as cnt
from b
)
select a.col, b.col
from a join
b
on mod(a.seqnum - 1, b.cnt) = b.seqnum - 1;
For more random assignment, randomize the seqnums:
with a as (
select a.*,
row_number() over (order by dbms_random.random) as seqnum
from a
order by db
),
b as (
select b.*, count(*) over () as cnt,
row_number() over (order by dbms_random.random) as seqnum
from b
)
select a.col, b.col
from a join
b
on mod(a.seqnum - 1, b.cnt) = b.seqnum - 1;
You can use the ROWNUM for achieving the same:
SELECT
COLOUR,
EMP
FROM
(
SELECT
COLOUR,
ROWNUM RN
FROM
T1
) T1,
(
SELECT
EMP,
ROWNUM RN
FROM
T2
) T2
WHERE
MOD(T1.RN, 2) + 1 = T2.RN
Fiddler SQL QUERY
Try this,
with Employees as
(select Emp, Row_Number() Over(order by 1) Rn
from B
cross join (select 1
from Dual
connect by level < (select count(1)
from A) / (select count(1)
from B) + 1)
order by Dbms_Random.Value),
Colours as
(select Colour, Rownum Rn
from A)
select t.Colour, k.Emp
from Colours t
join Employees k
on t.Rn = k.Rn

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;

How to select alternative rows from table in sql?

In table there is a column named status which can have either 1 or 2.
Now I want to select pattern like this
First Row 1
Second Row 2
Third Row 1
Fourth Row 2
......
For even rows
select * from emp where rowid%2 = 0;
For odd rows
select * from emp where rowid%2 != 0;
MS SQL:
select t1.*, (RN % 2)+1 as [STATUS] from
(
select t.*, ROW_NUMBER() OVER (ORDER BY <ORDER COLUMN NAME HERE>) as RN
) t1
Oracle:
SELECT * from
( SELECT a1.*,rank() over (partition BY status order by rownum) RNK FROM TABLE1 a1
)
ORDER BY rnk,status
Sqlfiddle:
try this
select * from (select table.* ,rownum k from table) where mod(k,2)<>0;
Try This:
SELECT e1.* FROM Employee e1
INNER JOIN
(
Select e2.*, ROW_NUMBER() OVER (ORDER BY <Column Names>) AS RN FROM Employee e2
)E2
ON e1.eid=e2.eid
where e2.RN%2=0
TO Get ODD records use following:
SELECT e1.* FROM Employee e1 INNER JOIN
( Select e2.*, ROW_NUMBER() OVER (ORDER BY ) AS RN FROM Employee e2 )E2 ON e1.eid=e2.eid where e2.RN%2=1

Sql Select top 2 , bottom 2 and 6 random records

How to select top 2 , bottom 2 and 6 random (not in Top 2 and Bottom 2) records of the table using one SQL select query?
In MS SQL 2005/2008:
with cte
as
(
select
row_number() over (order by name) RowNumber,
row_number() over (order by newid()) RandomOrder,
count(*) over() Total,
*
from sys.tables
)
select *
from cte
where RowNumber <= 2 or Total - RowNumber + 1 <= 2
union all
select *
from
(
select top 6 *
from cte
where RowNumber > 2 and Total - RowNumber > 2
order by RandomOrder
) tt
Replace sys.tables with your table name and alter order by name to specify order condition for top 2 and bottom 2.
Perhaps not a single select statment, but it can be executed in one call:
/* Top 2 - change order by to get the 'proper' top 2 */
SELECT * from table ORDER BY id DESC LIMIT 2
UNION ALL
/* Random 6.. You may want to add a WHERE and random data to get the random 6 */
/* Old Statement before edit - SELECT * from table LIMIT 6 */
SELECT * from table t
LEFT JOIN (SELECT * from table ORDER BY id DESC LIMIT 2) AS top ON top.id = t.id
LEFT JOIN (SELECT * from table ORDER BY id DESC LIMIT 2) AS bottom ON bottom.id = t.id
WHERE ISNULL(top.id ) AND ISNULL(bottom.id)
ORDER BY RANDOM()
LIMIT 6
UNION ALL
/* Bottom 2 - change order by to get the 'proper' bottom 2 */
SELECT * from table ORDER BY id ASC LIMIT 2
Something along those lines. Basically the UNION All is the trick.
Assuming the "order" is by the id column:
select * from (select id, id from my_table order by id limit 2) t1
union
select * from (select id, id from my_table where id not in (
select * from (select id from my_table order by id asc limit 2) t22
union
select * from (select id from my_table order by id desc limit 2 ) t23)
order by rand()
limit 6) t2
union
select * from (select id, id from my_table order by id desc limit 2) t3
EDIT: Fixed syntax and tested query - it works

Distinct select on Oracle

What i am trying to do is a simple recommender , must take the biggest weighted top 40 element's node2 element. Calculation for weight comes from (E.WEIGHT * K.GRADE). Now this code succesfully returns top 40 elements. However, i don't want E.NODE2 to return duplicates. POSTGRE SQL allowed me to do SELECT DISTINCT ON (NODE2) E.NODE2 , (E.WEIGHT * K.GRADE). How can i do the same in oracle?
The complete sql query;
SELECT *
FROM (SELECT DISTINCT E.NODE2 , (E.WEIGHT * K.GRADE)
FROM KUAISFAST K, EDGES E
WHERE K.ID = 1 AND K.COURSE_ID = E.NODE1 AND E.NODE2 NOT IN(
SELECT K2.COURSE_ID
FROM KUAISFAST K2
WHERE K2.ID = 1
)
ORDER BY( E.WEIGHT * K.GRADE ) DESC) TEMP
WHERE rownum <= 40
This should solve your problem, altough quite slow
SELECT * FROM
(SELECT *
FROM (SELECT E.NODE2 , max(E.WEIGHT * K.GRADE ) AS MAXDE
FROM KUAISFAST K, EDGES E
WHERE K.ID = 1 AND K.COURSE_ID = E.NODE1 AND E.NODE2 NOT IN(
SELECT K2.COURSE_ID
FROM KUAISFAST K2
WHERE K2.ID = 1
)
GROUP BY E.NODE2 )
ORDER BY MAXDE DESC)
WHERE rownum <= 40
I believe you want something like
SELECT *
FROM (
SELECT
E.NODE2,
(E.WEIGHT * K.GRADE),
ROW_NUMBER() OVER (PARTITION BY E.NODE2 ORDER BY E.WEIGHT * K.GRADE DESC) R
FROM
KUAISFAST K,
EDGES E
WHERE
K.ID = 1 AND
K.COURSE_ID = E.NODE1 AND
E.NODE2 NOT IN
( SELECT K2.COURSE_ID
FROM KUAISFAST K2
WHERE K2.ID = 1
)
ORDER BY (E.WEIGHT * K.GRADE) DESC
) TEMP
WHERE R=1 AND
ROWNUM <= 40
In your subselect, I think you want: MAX(E.WEIGTH * K.GRADE) so that only one value comes back for each E.NODE2.
This means you'll need to GROUP BY E.NODE2 as well.