How can I use if statement after a CTE (SQL Server 2005) - sql

Last night I was writing a simple T-SQL program something like this
DECLARE #ROLEID AS INT
SELECT #ROLEID = [ROLE ID] FROM TBLROLE
;WITH CTE
AS
(
SELECT * FROM SOMETABLE
)
IF (#ROLEID = 1)
BEGIN
//SOMECODE
END
ELSE IF(#ROLEID = 2)
BEGIN
//SOMECODE
END
ELSE
BEGIN
//SOMECODE
END
I found after compilation that it is throwing error something like "Incorrect statement near if"
What is wrong?
However, I did that by using some other way. But I wanted to know why it did not work!

Common table expressions are defined within the context of a single statement:
WITH cte_name AS (
<cte definition>)
<statement that uses cte>;
So you can do something like:
WITH CTE
AS
(
SELECT * FROM SOMETABLE
)
SELECT * FROM CTE;
or
WITH CTE
AS
(
SELECT * FROM SOMETABLE
)
UPDATE CTE
SET somefield = somevalue
WHERE id = somekey;
A CTE must be followed by a single
SELECT, INSERT, UPDATE, MERGE, or
DELETE statement that references some
or all the CTE columns. A CTE can also
be specified in a CREATE VIEW
statement as part of the defining
SELECT statement of the view

A little late but I can't be the only one bumping into this.
A solution could be to create a temporary table like this:
-- If previous run of this query fails, the temp table will be deleted.
-- Selecting into creates the temp table which fails if it already exists
IF EXISTS(SELECT [name] FROM tempdb.sys.tables WHERE [name] like '#dtBalansOpgesteldGefilterd%') BEGIN
DROP TABLE #temp
END;
;WITH CTE
AS
(
SELECT * FROM SOMETABLE
)
-- Followed by select statement as required
SELECT *
INTO #temp
FROM CTE
IF #awsome = 1
BEGIN
SELECT 'WHATEVERYOUWANT' AS WhateverColumnNameYouWant, *
FROM #temp
END

The closest you'll get is using a UNION ALL to do a crude switched select:
DECLARE #ROLEID AS INT
SELECT #ROLEID = [ROLE ID] FROM TBLROLE
;WITH CTE
AS
(
SELECT * FROM SOMETABLE
)
SELECT
--somecolumns
FROM
CTE
--other stuff too
WHERE
#ROLEID = 1
UNION ALL
SELECT
--somecolumns
FROM
CTE
--other stuff too
WHERE
#ROLEID = 2
UNION ALL
SELECT
--somecolumns
FROM
CTE
--other stuff too
WHERE
#ROLEID = 3
...
UNION ALL
SELECT
--somecolumns
FROM
CTE
--other stuff too
WHERE
#ROLEID = n

Try putting the CTE in the IF. It worked for me.
IF #awsome = 1
BEGIN
;WITH CTE
AS
(
SELECT * FROM SOMETABLE
)
SELECT 'WHATEVERYOUWANT' FROM CTE
END
ELSE IF #awesome = 2
BEGIN
;WITH CTE2
AS
(
SELECT * FROM SOMETABLE
)
SELECT 'WHATEVERYOUWANT' FROM CTE2
END

Related

Return scalar-value after WITH sql

I want to write a function that returns 0 or 1. How to do it, if I use WITH-construction in my code
ALTER FUNCTION [dbo].[IsBossFull] (#bossFull int, #user int)
RETURNS bit
AS
BEGIN
;WITH CTE
AS (
SELECT *
FROM [USERS] WHERE [Id] = #user
UNION ALL
SELECT U.*
FROM CTE C
INNER JOIN [USERS] u on U.[Id] = C.[Chief]
)
-- then something like
if exists(
select * from cte where id = #bossFull)
return 1
else return 0
END
You would assign the value to a variable and return that:
DECLARE #retval int;
WITH CTE AS (
SELECT *
FROM USERS
WHERE Id = #user
UNION ALL
SELECT U.*
FROM CTE C INNER JOIN
USERS u
ON U.[Id] = C.[Chief]
)
SELECT #retval = COUNT(*)
FROM cte
WHERE id = #bossfull;
RETURN(#retval);
I haven't actually simplified the code. You can stop the recursion when #bossful is actually found.
If you really want, you can use return(case when retval > 0 then 1 else 0 end). However, #retval cannot be greater than 1, because that would imply cycles in the hierarchy -- and the CTE would not return.

How to execute this SQL Query

I am having following scenario
Drop Table #Temp
Create Table #Temp(name1 text, name2 text)
Insert INTO #Temp Values ('test','test')
Insert INTO #Temp Values ('test','test')
Insert Into #Temp1 Select * From
(
;With CTE as (
Select * from #Temp
)
select * from CTE
)
I know we can't use CTE as subquery .. but for hard time I don't know exact syntax of subquery since it is being provided by other system.
just image this
Insert Into #Temp1 Select * From
(
"Query Provided by Other System"
)
So I don't have any control on subquery ("Query Provided by Other System").. And I have also tried dynamic sql query like
Declare #subquery nvarchar(max)
set #subquery=';With CTE2 as ( Select * from #Temp) select * from CTE2'
INSERT INTO #Temp1 From (EXEC sp_executesql #subquery)
This also gives error...
More Things to know:
i)I don't know about what are the columns will sub query returns
ii)And I don't have any control in sub query . like what is syntax of subquery and how it looks like?
so from these things, even I can't use dynamic sql (EXEC sp_executesql).because I don't know what will happen if #subquery itself contains dynamic sql.
Please help anyone...
Use like this, with dynamic sql. It will work...
Insert Into #Temp1
EXEC sp_executesql #subquery
You can use the below syntax
;With CTE as (
Select * from #Temp
)
Insert Into #Temp1
Select *
From (
select * from CTE
) t
or if you don't have already created the #Temp1 table, use below code. It will automatically create the #temp1 table according to the selected fields
;With CTE as (
Select * from #Temp
)
Select *
Into #Temp1
From (
select * from CTE
) t
Besides syntax, you can simplify above SQL code as follows
;With CTE as (
Select * from #Temp
)
Insert Into #Temp1
Select *
From CTE
or better
Insert Into #Temp1
Select * from #Temp

Using IF / ELSE to determine a SELECT INTO statement

I'm having some strange issues using IF / ELSE to determine which one or two SELECT statements to execute. The error message I'm getting when running the full statement is that my temporary table already exists, but that does not occur if I run two separate executions of two separate IF statements.
Here is the code in SQL Server:
IF (select BusinessDayCount from Calendartbl) <= 1
BEGIN
SELECT * into #temp1
FROM PreviousMonthTbl
END
ELSE
BEGIN
SELECT * into #temp1
FROM CurrentMonthTbl
END
It's a "feature" of the syntax checking in SQL Server. You simply cannot "create" a #temporary table twice within the same batch.
This is the pattern you need.
SELECT * into #temp1
FROM PreviousMonthTbl
WHERE 1=0;
IF (select BusinessDayCount from Calendartbl) <= 1
BEGIN
INSERT into #temp1 SELECT *
FROM PreviousMonthTbl
END
ELSE
BEGIN
INSERT into #temp1 SELECT *
FROM CurrentMonthTbl
END
If you prefer, you can also express the branch (in this case) as a WHERE clause:
SELECT * into #temp1
FROM PreviousMonthTbl
WHERE (select BusinessDayCount from Calendartbl) <= 1
UNION ALL
SELECT *
FROM CurrentMonthTbl
WHERE isnull((select BusinessDayCount from Calendartbl),2) > 1
You can't use SELECT INTO for a tables with same name in the same batch. Use a different name for a temporary table
IF EXISTS(
SELECT 1
FROM Calendartbl
WHERE BusinessDayCount <= 1
)
BEGIN
IF OBJECT_ID('tempdb.dbo.#PreviousMonthTbl') IS NULL DROP TABLE dbo.#PreviousMonthTbl
SELECT *
INTO #PreviousMonthTbl
FROM PreviousMonthTbl
END
ELSE
BEGIN
IF OBJECT_ID('tempdb.dbo.#CurrentMonthTbl') IS NULL DROP TABLE dbo.#CurrentMonthTbl
SELECT *
INTO #CurrentMonthTbl
FROM CurrentMonthTbl
END
From what I understand the problem is this:
When you run the below statement,
SELECT * into #temp1 FROM CurrentMonthTbl
you are creating a temp table on the fly.
If before that line you had a create table statement, then this Select into statement will fail because the table already exists.
If in your case you already have a temp table created, then try replacing:
SELECT * into #temp1 FROM CurrentMonthTbl
with:
Insert into #temp1
Select * from CurrentMonthTbl
Also look at There is already an object named '##Temp' in the database
You can use actual table in place of #temp1 table in if else statement. After that you can insert the data from actual to temp table and drop the actual table.
IF OBJECT_ID('tempdb..#temp1') is not null
drop table #temp1
IF (select BusinessDayCount from Calendartbl) <= 1
BEGIN
SELECT * into dbo.TempTable
FROM PreviousMonthTbl
END
ELSE
BEGIN
SELECT * into dbo.TempTable
FROM CurrentMonthTbl
END
select * into #temp1
from dbo.TempTable
IF OBJECT_ID('dbo.TempTable', 'U') is not null
drop table dbo.TempTable

How to use WITH result table multiple times?

DECLARE #count INT;
WITH CTE AS
(SELECT...)
SELECT #count = COUNT(ID) FROM CTE;
SELECT * FROM CTE
Can I use CTE table after first expression SELECT #count = COUNT(ID) FROM CTE? I get error Invalid object name 'CTE'.
I'm using SQL Server 2008.
Thanks
You can't, really; the CTE only exists for the scope of a single statement. However in this case you could do this:
DECLARE #count INT;
WITH CTE AS (SELECT...)
SELECT * FROM CTE;
SELECT #count = ##ROWCOUNT;
If you need the count as part of the resultset, then you can just say:
WITH CTE AS (SELECT...)
SELECT *, COUNT(*) OVER() FROM CTE;
If you need the count for other reasons, please describe them.
You could use a table variable and use it everywhere.
DECLARE #count INT;
DECLARE #CTE AS TABLE (<<columns>>)
INSERT INTO #CTE (<<columns>>)
SELECT ....
WITH CTE AS
(SELECT * FROM #CTE)
SELECT #count = COUNT(ID) FROM CTE;
SELECT * FROM #CTE

DELETE EXCEPT TOP 1

Is there any way to delete all the rows in a table except one (random) row, without specifying any column names in the DELETE statement?
I'm trying to do something like this:
CREATE TABLE [dbo].[DeleteExceptTop1]([Id] INT)
INSERT [dbo].[DeleteExceptTop1] SELECT 1
INSERT [dbo].[DeleteExceptTop1] SELECT 2
INSERT [dbo].[DeleteExceptTop1] SELECT 3
SELECT * FROM [dbo].[DeleteExceptTop1]
DELETE
FROM [dbo].[DeleteExceptTop1]
EXCEPT
SELECT TOP 1 * FROM [dbo].[DeleteExceptTop1]
SELECT * FROM [dbo].[DeleteExceptTop1]
The final SELECT should yield one row (could be any of the three).
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT newid())) AS RN
FROM [dbo].[DeleteExceptTop1]
)
DELETE FROM CTE
WHERE RN > 1
Or similar to #abatishchev's answer but with more variety in the ordering and avoiding deprecated constructs.
DECLARE #C INT
SELECT #C = COUNT(*) - 1
FROM [dbo].[DeleteExceptTop1]
IF #c > 0
BEGIN
WITH CTE AS
(
SELECT TOP(#C) *
FROM [dbo].[DeleteExceptTop1]
ORDER BY NEWID()
)
DELETE FROM CTE;
END
Or a final way that uses EXCEPT and assumes no duplicate rows and that all columns are of datatypes compatible with the EXCEPT operator
/*Materialise TOP 1 to ensure only evaluated once*/
SELECT TOP(1) *
INTO #T
FROM [dbo].[DeleteExceptTop1]
ORDER BY NEWID()
;WITH CTE AS
(
SELECT *
FROM [dbo].[DeleteExceptTop1] T1
WHERE EXISTS(
SELECT *
FROM #T
EXCEPT
SELECT T1.*)
)
DELETE FROM CTE;
DROP TABLE #T
Try:
declare #c int
select #c = count(*) - 1 from [dbo].[DeleteExceptTop1]
IF #c > 0
BEGIN
set RowCount #c
delete from [dbo].[DeleteExceptTop1]
END
No.
You need to use a column name (such as that of the primary key) to identify which rows you want to remove.
"random row" has no meaning in SQL except its data. If you want to delete everything except some row, you must differentiate that row from the others you with to DELETE
EXCEPT works by comparing the DISTINCT values in the row.
EDIT: If you can specify the primary key then this is a trivial matter. You can simply DELETE where the PK <> your "random" selection or NOT IN your "random" selection(s).
EDIT: Apparently I'm wrong about the need to specify any column name, you can do it using the assigned ROW_NUMBER.. But I'm not going to delete my answer because it references your use of EXCEPT which was discussed in the comments. You cannot do it without deriving some column name like that from ROW_NUMBER
You could do something like this (SQL 2008)
DECLARE #Original TABLE ([Id] INT)
INSERT INTO #Original(ID) VALUES(1)
INSERT INTO #Original(ID) VALUES(2)
INSERT INTO #Original(ID) VALUES(3)
SELECT * FROM #Original;
WITH CTE AS
(SELECT ROW_NUMBER() OVER(ORDER BY ID) AS ROW, ID FROM #Original)
DELETE #Original
FROM #Original O
INNER JOIN CTE ON O.ID = CTE.ROW
WHERE ROW > 1
SELECT * FROM #Original
It seems like the simplest answer may be the best. The following should work:
Declare #count int
Set #count=(Select count(*) from DeleteExceptTop1)-1
Delete top (#count) from DeleteExceptTop1
I know it has been answered but what about?
DELETE
FROM [dbo].[DeleteExceptTop1]
Where Id not in (
SELECT TOP 1 * FROM [dbo].[DeleteExceptTop1])