T-SQL: accessing temporary column in Common Table Expression - sql-server

Is it possible to access a temporary column that was defined in a query for a Common Table Expression? Say I have
select * from myTable
;with cte as
(
select
*, Salary * 4 as FourTimesSalary
from
Employees
where
Name = #name
and ID >= 100
)
Is there a way to use the temporary column FourTimesSalary when querying cte like so?
select top 2 *
from cte
order by FourTimesSalary, Name
TIA.

Yes you can do that. Example:
with temp as
(
select 1 as id, 2*4 as val
UNION
select 2 as id, 3*4 as val
)
SELECT * FROM temp ORDER BY VAL desc
Your example looks fine, did you get an error when you tried that or something?

Related

Subquery reference newly created column

I have been trying to write a subquery based on the SQL Server docs. I am trying to write a SQL Server query to
cast a varchar to an int
select rows where the varchar is null.
My logic is:
outer select+ where clause gets all rows where the new column (q5int) is NULL
create inner select that performs the cast and creates the new column (q5int)
What am I doing wrong? I would appreciate an explanation of whats wrong with my logic, not just how to fix my code.
select *
from
(select
*, cast (NULLIF(q5,'NULL') as int) as q5int
from
ft_merge)
where
q5int = NULL
You can do this sans the sub-query
select *
from ft_merge
WHERE try_convert(int,q5) is null
Let us analyze your query first
select * from (
select *,cast (NULLIF(q5,'NULL') as int) as q5int
from ft_merge
) WHERE q5int=NULL
1.NULLIF(q5,'NULL') should be NULLIF(q5,NULL) not in quotes
2.WHERE q5int=NULL should be replaced with "q5int IS NULL"
Creating test data
Select * into #ft_merge
from
(
Select 1 AS ID,'111' AS q5
union
Select 2, null
union
Select 3,'2222'
union
Select 4,null
)t
/* using your query */
select * from (
select *,cast(q5 as int) as q5int
from #ft_merge where ISNUMERIC(q5) > 0 or q5 is null
)t WHERE q5int is NULL
/* another optimal way of doing this */
select *,cast(q5 as int) as q5int
from #ft_merge where NULLIF(q5,NULL) is null

selecting random rows with normal distribution based on a column in SQL Server 2012

FULL DETAILS:
let me explain more clear. this is a table including about 100 question. every question has a BooKRange property that shows from which part of the book, this question hast fetched with values 1,2,3,4. and there is another property called Level that shows level of the difficulty of the question with values 1,2,3,4,5. now i need to randomly select 20 question that have to include all four Book Ranges and all five levels with a normal distribution.
please consider that i need to select distinct rows.
thank you very much.
edit: added the table
CREATE TABLE [dbo].[Question] (
[QuesID] INT IDENTITY (1, 1) NOT NULL,
[BookRange] NVARCHAR (50) NULL,
[Level] NVARCHAR (50) NULL,
PRIMARY KEY CLUSTERED ([QuesID] ASC)
);
You can do this query (assuming a uniform distribution) without doing a union. You just need to specify the ordering correctly.
If you want to select 5 questions from each of the levels, then you can do so by assigning a sequential number to the questions in each level. If these are assigned randomly, then you should meet the requirement of randomness for the levels:
with q as (
select q.*,
row_number() over (partition by [range] order by newid()) as seqnum
from Question q
)
select *
from q
where seqnum <= 5;
If you want to ensure that these is exactly one question for each level and range, but want the questions random, then do:
with q as (
select q.*,
row_number() over (partition by [range], [level] order by newid()) as seqnum
from Question q
)
select *
from q
where seqnum = 1;
By the way, range and level are reserved words in SQL Server. In general, it is good practice to avoid using reserved words for the names of things like tables, columns, stored procedures, and so on.
Select distinct id from table where level=1 order by rand() limit 5 union Select distinct id from table where level=2 order by rand() limit 5 union Select distinct id from table where level=3 order by rand() limit 5 union Select distinct id from table where level=4 order by rand() limit 5
Since you havent provided any table schema, Assuming we have a table dbo.Number with One column with values from 1 - 30 you could do something like this ...
;With NthGroups
AS
(
SELECT * , NTILE(4) OVER (ORDER BY Nums) Np
FROM dbo.Number
),
Top25Perc
AS
(
SELECT TOP 5 * FROM NthGroups
WHERE NP = 1
ORDER BY NEWID()
UNION ALL
SELECT TOP 5 * FROM NthGroups
WHERE NP = 2
ORDER BY NEWID()
UNION ALL
SELECT TOP 5 * FROM NthGroups
WHERE NP = 3
ORDER BY NEWID()
UNION ALL
SELECT TOP 5 * FROM NthGroups
WHERE NP = 4
ORDER BY NEWID()
)
SELECT * FROM Top25Perc
Update
Just read your comment in other answer and you have mentioned you have a column Range with values (1,2,3,4) , this makes query even simpler , you can do something like this
;With
RandTop5
AS
(
SELECT TOP 5 * FROM TableName
WHERE [Range] = 1
ORDER BY NEWID()
UNION ALL
SELECT TOP 5 * FROM TableName
WHERE [Range] = 2
ORDER BY NEWID()
UNION ALL
SELECT TOP 5 * FROM TableName
WHERE [Range] = 3
ORDER BY NEWID()
UNION ALL
SELECT TOP 5 * FROM TableName
WHERE [Range] = 4
ORDER BY NEWID()
)
SELECT * FROM RandTop5

Select Different Column Value for Row with Max Value

I'm hoping for a cleaner way to do something that I know how to do one way. I want to retrieve the UserId for the MAX ID value as well as that MAX ID value. Let's say I have a table with data like this:
ID UserId Value
1 10 'Foo'
2 15 'Blah'
3 10 'Blech'
4 20 'Qwerty'
I want to retrieve:
ID UserId
4 20
I know I could do this like so:
SELECT
t.ID,
t.UserID
FROM
(
SELECT MAX(ID) as [MaxID]
FROM table
) as m
JOIN table as t ON m.MaxID = t.ID
I'm only vaguely familiar with the ROW_NUMBER(), RANK() and other similar methods and I can't help believing that this scenario could benefit from some such method to get rid of joining back to the table.
You can definitely use ROW_NUMBER for something like this:
with t1Rank as
(
select *
, t1Rank = row_number() over (order by ID desc)
from t1
)
select ID, UserID
from t1Rank
where t1Rank = 1
SQL Fiddle with demo.
The advantage with this approach is you can bring Value (or other fields as required) into the result set, too. Plus you can tweak the ordering/grouping as required.
You could also just do it with a sub-query like this:
SELECT ID ,
UserID
FROM table
WHERE ID = ( SELECT MAX(ID)
FROM table
);
SELECT TOP 1 ID, UserID FROM <table> ORDER BY ID DESC

Need to return all columns from a table when using GROUP BY

I have a table let's say it has four columns
Id, Name, Cell_no, Cat_id.
I need to return all columns whose count of Cat_id is greater than 1.
The group should be done on Cell_no and Name.
What i have done so far..
select Cell_no, COUNT(Cat_id)
from TableName
group by Cell_Number
having COUNT(Cat_id) > 1
But what i need is some thing like this.
select *
from TableName
group by Cell_Number
having COUNT(Cat_id) > 1
Pratik's answer is good but rather than using the IN operator (which only works for single values) you will need to JOIN back to the result set like this
SELECT t.*
FROM tableName t
INNER JOIN
(SELECT Cell_no, Name
FROM TableName
GROUP BY Cell_no , Name
HAVING COUNT(Cat_id) > 1) filter
ON t.Cell_no = filter.Cell_no AND t.Name = filter.Name
you just need to modify your query like below --
select * from tableName where (Cell_no, Name) in (
select Cell_no, Name from TableName
Group by Cell_no , Name
having COUNT(Cat_id) > 1
)
as asked in question you want to group by Cell_no and Name.. if so you need to change your query for group by columns and select part also.. as I have mentioned
This version requires only one pass over the data:
SELECT *
FROM (SELECT a.*
,COUNT(cat_id) OVER (PARTITION BY cell_no)
AS count_cat_id_not_null
FROM TableName a)
WHERE count_cat_id_not_null > 1;

How do I select last 5 rows in a table without sorting?

I want to select the last 5 records from a table in SQL Server without arranging the table in ascending or descending order.
This is just about the most bizarre query I've ever written, but I'm pretty sure it gets the "last 5" rows from a table without ordering:
select *
from issues
where issueid not in (
select top (
(select count(*) from issues) - 5
) issueid
from issues
)
Note that this makes use of SQL Server 2005's ability to pass a value into the "top" clause - it doesn't work on SQL Server 2000.
Suppose you have an index on id, this will be lightning fast:
SELECT * FROM [MyTable] WHERE [id] > (SELECT MAX([id]) - 5 FROM [MyTable])
The way your question is phrased makes it sound like you think you have to physically resort the data in the table in order to get it back in the order you want. If so, this is not the case, the ORDER BY clause exists for this purpose. The physical order in which the records are stored remains unchanged when using ORDER BY. The records are sorted in memory (or in temporary disk space) before they are returned.
Note that the order that records get returned is not guaranteed without using an ORDER BY clause. So, while any of the the suggestions here may work, there is no reason to think they will continue to work, nor can you prove that they work in all cases with your current database. This is by design - I am assuming it is to give the database engine the freedom do as it will with the records in order to obtain best performance in the case where there is no explicit order specified.
Assuming you wanted the last 5 records sorted by the field Name in ascending order, you could do something like this, which should work in either SQL 2000 or 2005:
select Name
from (
select top 5 Name
from MyTable
order by Name desc
) a
order by Name asc
You need to count number of rows inside table ( say we have 12 rows )
then subtract 5 rows from them ( we are now in 7 )
select * where index_column > 7
select * from users
where user_id >
( (select COUNT(*) from users) - 5)
you can order them ASC or DESC
But when using this code
select TOP 5 from users order by user_id DESC
it will not be ordered easily.
select * from table limit 5 offset (select count(*) from table) - 5;
Without an order, this is impossible. What defines the "bottom"? The following will select 5 rows according to how they are stored in the database.
SELECT TOP 5 * FROM [TableName]
Well, the "last five rows" are actually the last five rows depending on your clustered index. Your clustered index, by definition, is the way that he rows are ordered. So you really can't get the "last five rows" without some order. You can, however, get the last five rows as it pertains to the clustered index.
SELECT TOP 5 * FROM MyTable
ORDER BY MyCLusteredIndexColumn1, MyCLusteredIndexColumnq, ..., MyCLusteredIndexColumnN DESC
Search 5 records from last records you can use this,
SELECT *
FROM Table Name
WHERE ID <= IDENT_CURRENT('Table Name')
AND ID >= IDENT_CURRENT('Table Name') - 5
If you know how many rows there will be in total you can use the ROW_NUMBER() function.
Here's an examble from MSDN (http://msdn.microsoft.com/en-us/library/ms186734.aspx)
USE AdventureWorks;
GO
WITH OrderedOrders AS
(
SELECT SalesOrderID, OrderDate,
ROW_NUMBER() OVER (ORDER BY OrderDate) AS 'RowNumber'
FROM Sales.SalesOrderHeader
)
SELECT *
FROM OrderedOrders
WHERE RowNumber BETWEEN 50 AND 60;
In SQL Server 2012 you can do this :
Declare #Count1 int ;
Select #Count1 = Count(*)
FROM [Log] AS L
SELECT
*
FROM [Log] AS L
ORDER BY L.id
OFFSET #Count - 5 ROWS
FETCH NEXT 5 ROWS ONLY;
Try this, if you don't have a primary key or identical column:
select [Stu_Id],[Student_Name] ,[City] ,[Registered],
RowNum = row_number() OVER (ORDER BY (SELECT 0))
from student
ORDER BY RowNum desc
You can retrieve them from memory.
So first you get the rows in a DataSet, and then get the last 5 out of the DataSet.
There is a handy trick that works in some databases for ordering in database order,
SELECT * FROM TableName ORDER BY true
Apparently, this can work in conjunction with any of the other suggestions posted here to leave the results in "order they came out of the database" order, which in some databases, is the order they were last modified in.
select *
from table
order by empno(primary key) desc
fetch first 5 rows only
Last 5 rows retrieve in mysql
This query working perfectly
SELECT * FROM (SELECT * FROM recharge ORDER BY sno DESC LIMIT 5)sub ORDER BY sno ASC
or
select sno from(select sno from recharge order by sno desc limit 5) as t where t.sno order by t.sno asc
When number of rows in table is less than 5 the answers of Matt Hamilton and msuvajac is Incorrect.
Because a TOP N rowcount value may not be negative.
A great example can be found Here.
i am using this code:
select * from tweets where placeID = '$placeID' and id > (
(select count(*) from tweets where placeID = '$placeID')-2)
In SQL Server, it does not seem possible without using ordering in the query.
This is what I have used.
SELECT *
FROM
(
SELECT TOP 5 *
FROM [MyTable]
ORDER BY Id DESC /*Primary Key*/
) AS T
ORDER BY T.Id ASC; /*Primary Key*/
DECLARE #MYVAR NVARCHAR(100)
DECLARE #step int
SET #step = 0;
DECLARE MYTESTCURSOR CURSOR
DYNAMIC
FOR
SELECT col FROM [dbo].[table]
OPEN MYTESTCURSOR
FETCH LAST FROM MYTESTCURSOR INTO #MYVAR
print #MYVAR;
WHILE #step < 10
BEGIN
FETCH PRIOR FROM MYTESTCURSOR INTO #MYVAR
print #MYVAR;
SET #step = #step + 1;
END
CLOSE MYTESTCURSOR
DEALLOCATE MYTESTCURSOR
Thanks to #Apps Tawale , Based on his answer, here's a bit of another (my) version,
To select last 5 records without an identity column,
select top 5 *,
RowNum = row_number() OVER (ORDER BY (SELECT 0))
from [dbo].[ViewEmployeeMaster]
ORDER BY RowNum desc
Nevertheless, it has an order by, but on RowNum :)
Note(1): The above query will reverse the order of what we get when we run the main select query.
So to maintain the order, we can slightly go like:
select *, RowNum2 = row_number() OVER (ORDER BY (SELECT 0))
from (
select top 5 *, RowNum = row_number() OVER (ORDER BY (SELECT 0))
from [dbo].[ViewEmployeeMaster]
ORDER BY RowNum desc
) as t1
order by RowNum2 desc
Note(2): Without an identity column, the query takes a bit of time in case of large data
Get the count of that table
select count(*) from TABLE
select top count * from TABLE where 'primary key row' NOT IN (select top (count-5) 'primary key row' from TABLE)
If you do not want to arrange the table in ascending or descending order. Use this.
select * from table limit 5 offset (select count(*) from table) - 5;

Resources