How to execute the GO statement with dynamic count? - sql

How to set dynamic count for GO statement?
I am getting the following error:
A fatal scripting error occurred.Incorrect syntax was encountered
while parsing Go.
when I tried to run the below query:
Declare #count int
Select #count=COUNT(*) From Users
Insert Into #DummyUsers
Select * from Users where UserName = 'Sachin'
GO #Count
But the same is working fine when I use the below query with hard coded count.
Declare #count int
Select #count=COUNT(*) From Users
Insert Into #DummyUsers
Select * from Users where UserName = 'Sachin'
GO 5
Appreciate your suggestions if you have any idea on this.

You can't. As soon as SSMS encounters GO the batch is terminated and your variable no longer exists.

You can't use a variable for the count parameter to GO, but in your example (which may be contrived) you could just join back to Users :
Insert Into #DummyUsers
Select U.* from Users U
INNER JOIN Users U2
ON U.UserName = 'Sachin'
Other options:
Dynaimc SQL (building up SQL by concatenating strings) and executing via SQLCMD.EXE or OSQL.EXE
Using a WHILE loop with a counter

If you simply want to insert a repeated row you could use a CTE or numbers table.
-- Sample data.
declare #Users as Table ( UserId Int Identity, Name VarChar(16) );
insert into #Users ( Name ) values
( 'Bob' ), ( 'Carol' ), ( 'Ted' ), ( 'Alice' );
select * from #Users;
-- Load another table with repetitions of a single user.
declare #TempUsers as Table ( UserId Int, Name VarChar(16) );
declare #Repetitions as Int = ( select Count(*) from #Users );
with TempUsers as (
select UserId, Name, 1 as Repetitions
from #Users
where Name = 'Ted'
union all
select UserId, Name, Repetitions + 1
from TempUsers
where Repetitions < #Repetitions
)
insert into #TempUsers ( UserId, Name )
select UserId, Name
from TempUsers;
select * from #TempUsers;

Instead try this.
DECLARE #cntr INT=1
WHILE #cntr <= #count
BEGIN
INSERT INTO #DummyUsers
SELECT *
FROM Users
WHERE UserName = 'Sachin'
SET #cntr+=1
END

I would just loop it
Declare #count int
Select #count=COUNT(*) From Users
WHILE(#count > 0)
BEGIN
Insert Into #DummyUsers
Select *
FROM Users
WHERE UserName = 'Sachin'
SET #count = #count - 1;
END

While I agree with the others that there is likely a better way to achieve what you are trying to do, if there is some limitation that we are not seeing, you could look into using a sequence
The sequence you create persists and can be reset as needed and you can "increment" it by calling the NEXT VALUE FOR function

Related

SQL Server run SELECT for each in list

I won't be surprised if SQL just doesn't work this way at all, but:
If we run two SELECT statements in a query, we get a split "Results" pane. I'm wondering if I can add variables to a list, and then have the number of result pane splits match the length of that list.
If I were to mix languages:
id_list = [26275, 54374, 84567]
for i in id_list:
SELECT * FROM table WHERE id = i
I'm just trying to easily compare results of a query while keeping distinct groups, with a changing number of variables. Since loops never seem to be the answer in SQL, I'd be just as happy inserting something like a blank line or horizontal rule, etc. Not sure if that's possible either though...
There is no concept of "lists" (as a separate data structure) in T-SQL. Does this do what you want?
SELECT *
FROM table
WHERE id IN (26275, 54374, 84567);
declare #i int = 0;
declare #Id int;
declare #Ids table (Id int);
insert #Ids select Id from (values (26275), (54374), (84567)) t(Id);
-- OR: insert #Ids select * from string_split('26275, 54374, 84567', ',');
declare #Count int = (select count(*) from #Ids);
while #i < #Count
begin
select #Id = Id, #i = #i + 1
from #Ids order by Id
offset #i rows fetch next 1 rows only;
select * from dbo.MyTable where Id = #Id;
end
You can use UNION ALL:
SELECT * FROM table WHERE id = 26275
UNION ALL
SELECT * FROM table WHERE id = 54374
UNION ALL
SELECT * FROM table WHERE id = 84567

SQL dynamic columns and Update multiple columns

I have a table UserPermission which has a number of columns of TINYINT type. e.g Read, Write, Update, Delete, Access etc.
I get three parameters in the stored procedure: #UserId, #ColNames, #ColValues where #ColNames and #ColValues are comma separated values.
How can I insert or update the table row (if already exists) with the passed column names and corresponding values.
I try to write the dynamic query which runs fine for INSERT but I was unable to write the UPDATE query dynamically with each column and its value to be concatenate.
Any response would be appreciated
Thanks in advance.
This is a somewhat dirty way to do what you require. However, if you create the following Stored Procedure:
CREATE FUNCTION [dbo].[stringSplit]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
You can then use that Procedure to join the data together:
DECLARE #TotalCols INT
DECLARE #TotalVals INT
SET #TotalCols = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('department, teamlead', ',')
);
SET #TotalVals = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('IT, Bob', ',')
);
IF #TotalCols = #TotalVals
BEGIN
IF OBJECT_ID('tempdb..#temptable') IS NOT NULL
DROP TABLE #temptable
CREATE TABLE #temptable (
ColName VARCHAR(MAX) NULL
,ColValue VARCHAR(MAX) NULL
)
INSERT INTO #temptable
SELECT a.DATA
,b.DATA
FROM dbo.stringSplit('department, teamlead', ',') AS a
INNER JOIN dbo.stringSplit('IT, Bob', ',') AS b ON a.Id = b.Id
SELECT *
FROM #temptable;
END
It's not very efficient, but it will bring you the desired results.
You can then use the temp table to update, insert and delete as required.
Instead of having a comma delimited list I would create a separate parameter for each Column and make its default value to NULL and in the code update nothing if its null or insert 0. Something like this....
CREATE PROCEDURE usp_UserPermissions
#UserID INT
,#Update INT = NULL --<-- Make default values NULL
,#Delete INT = NULL
,#Read INT = NULL
,#Write INT = NULL
,#Access INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #t TABLE (UserID INT, [Update] INT,[Read] INT
,[Write] INT,[Delete] INT,[Access] INT)
INSERT INTO #t (Userid, [Update],[Read],[Write],[Delete],[Access])
VALUES (#UserID , #Update , #Read, #Write , #Delete, #Access)
IF EXISTS (SELECT 1 FROM UserPermission WHERE UserID = #UserID)
BEGIN
UPDATE up -- Only update if a value was provided else update to itself
SET up.[Read] = ISNULL(t.[Read] , up.[Read])
,up.[Write] = ISNULL(t.[Write] , up.[Write])
,up.[Update] = ISNULL(t.[Update] , up.[Update])
,up.[Delete] = ISNULL(t.[Delete] , up.[Delete])
,up.[Access] = ISNULL(t.[Access] , up.[Access])
FROM UserPermission up
INNER JOIN #t t ON up.UserID = t.UserID
END
ELSE
BEGIN
-- if already no row exists for that User add a row
-- If no value was passed for a column add 0 as default
INSERT INTO UserPermission (Userid, [Update],[Read],[Write],[Delete],[Access])
SELECT Userid
, ISNULL([Update], 0)
, ISNULL([Read], 0)
, ISNULL([Write], 0)
, ISNULL([Delete], 0)
, ISNULL([Access], 0)
FROM #t
END
END

dynamic alias in sql server

I want query field with different alias in stored procedure
select COUNT(EmpCode) as CountEmp+#para
result shoud be
CountEmp1
45
CountEmp2
54
CountEmp1
76
Query loop in c# code:
select COUNT(EmpCode) where something = #something as CountEmp+#para
Approach without dynamic SQL:
--I create temp table for demonstration
DECLARE #some_table TABLE (
Something int,
EmpCode INT
)
INSERT INTO #some_table (Something, EmpCode)
VALUES (1, 10),(1, 22),(1, 12),(2, 12),(2, 30),(3, 65),(3, 15),(3, 11),(3, 5)
--Declare parameter we want to search
DECLARE #param int = 1
--Query
--In cte we select what we need based on parameter
;WITH cte AS (
SELECT 'CountEmp'+CAST(#param as nvarchar(10)) as SomeThing,
CAST(COUNT(EmpCode) as nvarchar(10)) as EmpCodeCount,
ROW_NUMBER() OVER (ORDER BY SomeThing ) as rn
FROM #some_table
WHERE SomeThing = #param
GROUP BY SomeThing
)
--And here comes UNION
SELECT SomeThing as Result
FROM (
SELECT SomeThing,rn
FROM cte
UNION ALL
SELECT EmpCodeCount ,rn
FROM cte
) as t
ORDER BY rn, SomeThing DESC
Output:
Result
------------------
CountEmp1
3
(2 row(s) affected)
Please try to make use of below code. Its working fine with SQL Server 2012.
IF OBJECT_ID ('temp..#Mytable') IS NOT NULL
CREATE TABLE #Mytable (ID INT IDENTITY (1,1),EmpCode INT)
DECLARE #max int ,#count int
SET #max =0;
DECLARE #str varchar(10)
INSERT #Mytable
(EmpCode)
VALUES
(10),
(45),
(35),
(63),
(56),
(65)
SET #count = (SELECT COUNT (ID) FROM #Mytable )
WHILE #count > #max
BEGIN
SET #max = #max+1
SET #str = CONVERT(varchar(10),#max)
EXEC('SELECT EmpCode AS Empcode'+#str+ ' FROM #Mytable WHERE ID = '+#str)
END

Error using Common Table Expression in SQL User Defined Function

CREATE FUNCTION [dbo].[udfGetNextEntityID]
()
RETURNS INT
AS
BEGIN
;WITH allIDs AS
(
SELECT entity_id FROM Entity
UNION SELECT entity_id FROM Reserved_Entity
)
RETURN (SELECT (MAX(entity_id) FROM allIDs )
END
GO
SQL isn't my strong point, but I can't work out what I'm doing wrong here. I want the function to return the largest entity_id from a union of 2 tables. Running the script gives the error:
Incorrect syntax near the keyword 'RETURN'.
I looked to see if there was some restriction on using CTEs in functions but couldn't find anything relevant. How do I correct this?
CREATE FUNCTION [dbo].[udfGetNextEntityID]()
RETURNS INT
AS
BEGIN
DECLARE #result INT;
WITH allIDs AS
(
SELECT entity_id FROM Entity
UNION SELECT entity_id FROM Reserved_Entity
)
SELECT #result = MAX(entity_id) FROM allIDs;
RETURN #result;
END
GO
While you can do it, why do you need a CTE here?
RETURN
(
SELECT MAX(entity_id) FROM
(
SELECT entity_id FROM dbo.Entity
UNION ALL
SELECT entity_id FROM dbo.Reserved_Entity
) AS allIDs
);
Also there is no reason to use UNION instead of UNION ALL since this will almost always introduce an expensive distinct sort operation. And please always use the schema prefix when creating / referencing any object.
You can not return the way your are doing from the function.
Make use of a local variable and return the same.
CREATE FUNCTION [dbo].[udfGetNextEntityID]()
RETURNS INT
AS
BEGIN
DECLARE #MaxEntityId INT;
WITH allIDs AS
(
SELECT entity_id FROM Entity
UNION SELECT entity_id FROM Reserved_Entity
)
SELECT #MaxEntityId = MAX(entity_id) FROM allIDs;
RETURN #MaxEntityId ;
END
GO
create function tvfFormatstring (#string varchar(100))
returns #fn_table table
(id int identity(1,1),
item int)
as
begin
insert into #fn_table(item)
declare #result int
set #string = #string+'-'
;with cte (start,number)
as
(
select 1 as start , CHARINDEX('-',#string,1) as number
union all
select number+1 as start , CHARINDEX('-',#string,number+1) as number from cte
where number <= LEN(#string)
)
select #result = SUBSTRING(#string,start,number-start) from cte ;
return #result;
end
select * from tvfFormatstring ('12321-13542-15634')

How to return default value from SQL query

Is there any easy way to return single scalar or default value if query doesn't return any row?
At this moment I have something like this code example:
IF (EXISTS (SELECT * FROM Users WHERE Id = #UserId))
SELECT Name FROM Users WHERE Id = #UserId
ELSE
--default value
SELECT 'John Doe'
How to do that in better way without using IF-ELSE?
Assuming the name is not nullable and that Id is unique so can match at most one row.
SELECT
ISNULL(MAX(Name),'John Doe')
FROM
Users
WHERE
Id = #UserId
Try ISNULL or COALESCE:
SELECT ISNULL((SELECT TOP 1 Name FROM Users WHERE Id = #UserId), 'John Doe')
The inner select will return nothing if no user exist with this id, the isnull will solve this case.
Try this
SELECT IFNULL(Name,'John Doe')
FROM Users
WHERE Id = #UserId)
You can drop the if statement using following construct but that doesn't necessarely mean it is better.
SELECT Name FROM Users WHERE Id = #UserId UNION ALL
SELECT 'John Doe' WHERE NOT EXISTS (SELECT Name FROM Users WHERE Id = #UserId)
Try isnull
SELECT IsNULL(Name, 'John Doe') FROM Users WHERE Id = #UserId
Edit:
drop table users
go
create table users
(id int,name varchar(20))
go
insert into users select 1,'1'
go
declare #userid int
set #userid = 1
select isnull(username.username, 'John Doe')
from (select #userid as userid) userid
outer apply (SELECT name as username FROM Users WHERE Id = userid.userid ) username
--outer apply (SELECT name as username FROM Users WHERE Id = #userid ) username
I suppose you could use ##ROWCOUNT to see if any will be returned.
SELECT Name FROM Users WHERE Id = #UserId
if(##ROWCOUNT = 0)
SELECT 'John Doe'
You could also use a variable if you're expecting one row.
declare #name varchar(100)
set #name = (select top 1 name from users where id = #userId)
if(#name is null) set #name = 'John Doe'
select #name
I would suggest that the best way to do is that first declare #name . Then set this value based on user id and then if #name is null show default name otherwise show name... That method would be as efficient as any other method and will be more readable.for other methods any other user to have think a lot to know what is going on unless there is nice comment.
declare #userid int
set #userid = 1
select isnull(
(select name from users where id = #userid),
'John Doe'
)
go
--My preffered would be this one..
declare #name varchar(20),#userid int
set #userid = 1
select #name = name from users where id = #userid
select isnull(#name,'John Doe')
If the query is supposed to return at most one row, use a union with the default value then a limit:
SELECT * FROM
(
SELECT Name FROM Users WHERE Id = #UserId`
UNION
SELECT 'John Doe' AS Name --Default value
) AS subquery
LIMIT 1
Both the query and default can have as many columns as you wish, and you do not need to guarantee they are not null.