Set the temporary table in SQL server as variable - sql

I am trying to create a temporary table but I need to change the name of the table based on the content of a variable as I need to create more than one table inside a while loop. Is there a way to do that?
while(#roCounter <= #insideCounter)
BEGIN
set #nameOfTable = #nameOfTable+#roCounter
select * into ##nameOfTable from #CC where ro=#roCounter --#nameOfTable is a variable
set #roCounter = #roCounter +1
END
Drop Table #CC
set #counter = #counter +1
END

Related

How to Create a table on variable name in SQL Server?

WHILE #i < #deptcount + 1
BEGIN
--creating dynamic tables
DECLARE #tablenames NVARCHAR(50)
SET #tablenames = 'dept' + Cast(#i AS NVARCHAR)
EXECUTE ('create table '+#tablenames+
' (deptno int, formno int, stdpr int, agg int)')
SET #i = #i + 1
END
Your code seems to work:
DECLARE #i INT = 0, #deptcount INT = 4;
while #i < #deptcount+1
Begin
--creating dynamic tables
declare #tablenames nvarchar(50)
set #tablenames = '##dept'+CAST(#i as nvarchar)
execute('create table '+#tablenames+' (deptno int, formno int, stdpr int, agg int)')
set #i = #i +1
End
SELECT *
FROM ##dept1
UNION ALL
SELECT *
FROM ##dept2
UNION ALL
SELECT *
FROM ##dept3;
LiveDemo
But reconsider your approach:
CREATE TABLE #tbl
The desire here is to create a table of which the name is determined
at run-time.
If we just look at the arguments against using dynamic SQL in stored
procedures, few of them are really applicable here. If a stored
procedure has a static CREATE TABLE in it, the user who runs the
procedure must have permissions to create tables, so dynamic SQL will
not change anything. Plan caching obviously has nothing to do with it.
Etc.
Nevertheless: Why? Why would you want to do this? If you are creating
tables on the fly in your application, you have missed some
fundamentals about database design. In a relational database, the set
of tables and columns are supposed to be constant. They may change
with the installation of new versions, but not during run-time.
Sometimes when people are doing this, it appears that they want to
construct unique names for temporary tables. This is completely
unnecessary, as this is a built-in feature in SQL Server. If you say:
CREATE TABLE #nisse (a int NOT NULL)
then the actual name behind the scenes will be something much longer,
and no other connections will be able to see this instance of #nisse.

SQL Server stored procedure error . invalid identifier

I have a stored procedure, but when I execute it from my front-end I get this error:
The name 'CREATE TABLE tmp_148_58 (affili_item_id
varchar(250),academic_id varchar(250),college_id
varchar(250),item_value_quantity_college_entry
varchar(250),item_value_notes_college_entry
varchar(250),college_enter_on varchar(250),college_enter_by
varchar(250),affili_category_colleges_autoid varchar(20))' is not a
valid identifier.
My procedure code:
ALTER PROCEDURE [dbo].[SpPortal_AppForAffi_Upd_Both_Lbl_And_Vals1]
(#columnList TEXT
,#insScript nvarchar(1000)
,#collegeId INT
,#LoginId BIGINT)
AS
BEGIN
DECLARE
#tmpTableName VARCHAR(200),
#effectCount INT = 0,
#effectCountTotal INT = 0,
#ExeQuery nvarchar(1000),
#InsertQuery nvarchar(1000)
SET #tmpTableName = CONCAT('#tmp_',#collegeId,'_',#LoginId);
SET #ExeQuery = CONCAT('DROP TABLE IF EXISTS ', #tmpTableName);
EXECUTE #ExeQuery ;
-- create temp table.. --
SET #ExeQuery = CONCAT ('CREATE TABLE ' , #tmpTableName , ' (',#columnList,')' ) ; -- here column list should be come from froent end...
EXECUTE #ExeQuery;
-- # create temp table.. --
-- load data into temp table --
SET #InsertQuery = CONCAT(' ' , #insScript);
EXECUTE #InsertQuery;
-- # load data into temp table.. --
-- updating affili_items_colleges master table--
SET #effectCount=0;
-- SET param_sp_success=0;
Begin TRANSACTION
Begin Try
-- SET param_sp_success = 0;
SET #effectCount = 0;
SET #effectCountTotal = 0;
SET #ExeQuery = CONCAT(' UPDATE ', #tmpTableName,' AS tmp ,affili_item_label afil,affili_items afi
SET afil.item_lable_name = tmp.item_value_quantity_college_entry
,afil.enter_on=tmp.college_enter_on
,afil.enter_by= tmp.college_enter_by
WHERE tmp.affili_item_id=afil.affili_item_id AND tmp.affili_item_label_id = afil.affili_item_label_id
AND afi.is_label = 1 AND tmp.academic_id=afil.academic_id AND tmp.college_id=afil.college_id
AND tmp.affili_item_id = afi.affili_item_id AND afi.active_status = 1 ');
EXECUTE #ExeQuery;
SET #ExeQuery = CONCAT(' UPDATE ', #tmpTableName,' AS tmp ,affili_items_colleges afic,affili_items afi
SET afic.item_value_quantity_college_entry = tmp.item_value_quantity_college_entry
,afic.item_value_notes_college_entry=tmp.item_value_notes_college_entry
,afic.college_enter_on=tmp.college_enter_on
,afic.college_enter_by= tmp.college_enter_by
WHERE tmp.affili_item_id=afic.affili_item_id AND tmp.affili_item_label_id = afic.affili_item_label_id
AND tmp.academic_id=afic.academic_id AND tmp.college_id=afic.college_id
AND tmp.affili_item_id = afi.affili_item_id AND afi.is_label <> 1 AND afi.active_status = 1 ');
EXECUTE #ExeQuery;
declare #te int=0
SET #ExeQuery = CONCAT ('SELECT COUNT(tem.affili_item_id) INTO #te
FROM ',#tmpTableName,' tem INNER JOIN affili_items afi ON tem.affili_item_id = afi.affili_item_id AND afi.is_label <> 1
WHERE afi.active_status = 1 ') ;
EXECUTE #ExeQuery;
SET #effectCount=0;
SET #effectCount = #te ;
IF(#effectCount>0)
BEGIN
SET #effectCountTotal= #effectCount+1;
END
-- SET param_sp_success = effectCountTotal;
IF(##TRANCOUNT>0)
BEGIN
COMMIT TRANSACTION
END
ELSE
BEGIN
ROLLBACK TRANSACTION
END
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
END
Can anyone help me out in solving it? I converted above query from mysql to SQL Server.
First of all - I really wondering why you are using all of this dynamically created statements. As I can see from your script - the only reason for it is unique name of temporary table you're creating.
But you don't really need your temporary table to have unique name, as this table is visible only in the scope of stored procedure where it was created (and also in the scope of 'child' procedures called from that one).
Also, as per your error it looks like your script tries to create real, not temporary table - see CREATE TABLE tmp_148_58 - name of table doesn't contains #. So you may have no right to create real tables under account you've running your sp.
I suggest you to rewrite your code without that confusing dynamics and error should go away ;)

How to set two dynamic temporary global variables for triggers in SQL server?

I have a stored procedure to update data, which is being called through C#. I also have several triggers set to my table which insert audit information to audit table when the data is changed.
The problem is I have two columns in audit table called changedBy(UserName) and changeSetID, which I can only retrieve them from C#, I can pass them as parameters to the stored procedure, then I don't know how to let trigger get the value of these two variables.
Originally I use context_info as a global variable, but I need to pass two variables to trigger.
Several approaches:
1) As granadaCoder, suggested, add those columns to your updated table, so that the trigger will be able to access them
2) Disable the trigger at the beginning of your proc, include the inserts to the audit tables in your proc (instead of letting the trigger do them), then re-enable the trigger at the end.
3) Keep this information in a permanent table. Have a table with the UniqueID of your updated table, and the "changedBy" and "changeSetID" columns. At the beginning of the proc, update this table with the parameters passed from C#. In the trigger, join inserted to this table to get the values of the two columns you need.
I actually figured out a workaround about this.
First, I pass my username and changesetID from C# to SQL stored procedure as parameters. Inside my stored procedure, I set CONTEXT_INFO to the combination of userName and changeSetID
Declare #userAndSetID VARBINARY(128)
SET #UserName = #UserName + ',' + #ChangeSetID + ','
SET #userAndSetID = CAST(CAST(#UserName AS NVARCHAR(MAX)) AS VARBINARY(128));
SET CONTEXT_INFO #userAndSetID
And Inside my trigger, I retrieve my two variables from CONTEXT_INFO(), by using split string function:
select #ChangedBy = value from dbo.fn_Split((CAST (CONTEXT_INFO() AS NVARCHAR(MAX))),',') where position = 1;
select #changesetID = value from dbo.fn_Split((CAST (CONTEXT_INFO() AS NVARCHAR(MAX))),',') where position = 2;
And here is the split string function, it gets a string separated by whatever symbol, and put them to a table variable row by row:
CREATE FUNCTION [dbo].[fn_Split](#text nvarchar(MAX), #delimiter nvarchar(20) = ' ')
RETURNS #Strings TABLE
(
position int IDENTITY PRIMARY KEY,
value nvarchar(1500)
)
AS
BEGIN
DECLARE #index int
SET #index = -1
WHILE (LEN(#text) > 0)
BEGIN
SET #index = CHARINDEX(#delimiter , #text)
IF (#index = 0) AND (LEN(#text) > 0)
BEGIN
INSERT INTO #Strings VALUES (#text)
BREAK
END
IF (#index > 1)
BEGIN
INSERT INTO #Strings VALUES (LEFT(#text, #index - 1))
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
ELSE
SET #text = RIGHT(#text, (LEN(#text) - #index))
END
RETURN
END
This is just a workaround, as #Tab Alleman suggested, the best way to solve this problem is to save the variables somewhere inside database, but since I don't want to modify the database at the moment, this solution will do the trick.
There is now a better way to do this, if SQL 2016 is being used Through SESSION_CONTEXT
Insert
EXEC sys.sp_set_session_context #key = N'ID', #value = 'Some Text', #read_only = 1;
Read Out
PRINT SESSION_CONTEXT(N'ID');
And for the second option you could specify a different Key

insert the null record in the print lable

I am trying to create a SP which print the label of my vendor, vendor name. I want the user set the startposition, before the startposition I just simply insert a null value. I want be able to reuse the label sheet.
I have the SP code like this:
Alter PROCEDURE [dbo].[z_sp_APVendorLabel]
(#VendorGroup bGroup ,
#StartPosition int)
AS
BEGIN
SET NOCOUNT ON;
Create table #data_null
(Vendor int,
Name varchar(60)null)
Declare #counter int
SET #counter = 0
WHILE #counter < #StartPosition
BEGIN
UPDATE #data_null SET Vendor='',Name=' '
SET #counter = #counter + 1
END
Create table #detial
(Vendor int,
Name varchar (60)null)
select Vendor, Name into #data from APVM
WHERE VendorGroup= #VendorGroup
select * from #data_null
Union All
select * from #detial
END
It is very simple, but when I test it, I did not get any data.
You're creating the table #data_null, and updating it, but never inserting any rows. If you inspect ##rowcount after each update, you'll see it's zero.
Before you change that loop to insert instead of update, please consider setting up a permanent table to select from. A loop to generate N values on every invocation of the procedure is really not the best use of your server's time, or yours. ;-)

I need to run a stored procedure on multiple records

I need to run a stored procedure on a bunch of records. The code I have now iterates through the record stored in a temp table. The stored procedure returns a table of records.
I was wondering what I can do to avoid the iteration if anything.
set #counter = 1
set #empnum = null
set #lname = null
set #fname = null
-- get all punches for employees
while exists(select emp_num, lname, fname from #tt_employees where id = #counter)
begin
set #empnum = 0
select #empnum = emp_num, #lname = lname , #fname= fname from #tt_employees where id = #counter
INSERT #tt_hrs
exec PCT_GetEmpTimeSp
empnum
,#d_start_dt
,#d_end_dt
,#pMode = 0
,#pLunchMode = 3
,#pShowdetail = 0
,#pGetAll = 1
set #counter = #counter + 1
end
One way to avoid this kind of iteration is to analyze the code within the stored procedure and revised so that, rather than processing for one set of inputs at a time, it processes for all sets of inputs at a time. Often enough, this is not possible, which is why iteration loops are not all that uncommon.
A possible alternative is to use APPLY functionality (cross apply, outer apply). To do this, you'd rewrite the procedure as one of the table-type functions, and work that function into the query something like so:
INSERT #tt_hrs
select [columnList]
from #tt_employees
cross apply dbo.PCT_GetEmpTimeFunc(emp_num, #d_start_dt, #d_end_dt, 0, 3, 0, 1)
(It was not clear where all your inputs to the procedure were coming from.)
Note that you still are iterating over calls to the function, but now it's "packed" into one query.
I think you are on the right track.
you can have a temp table with identity column
CREATE TABLE #A (ID INT IDENTITY(1,1) NOT NULL, Name VARCHAR(50))
After records are inserted in to this temp table, find the total number of records in the table.
DECLARE #TableLength INTEGER
SELECT #TableLength = MAX(ID) FROM #A
DECLARE #Index INT
SET #Index = 1
WHILE (#Index <=#TableLength)
BEGIN
-- DO your work here
SET #Index = #Index + 1
END
Similar to what you have already proposed.
Alternative to iterate over records is to use CURSOR. CURSORS should be avoided at any cost.