I am getting an error stating that I Must declare the table variable "#tempattend".
Please help me. How can I pass #tempattend table to #tempTableSelect variable?
ALTER PROCEDURE [dbo].[Rl_LM_AHS]
#SupEmpID nvarchar(10),
#SectorName nvarchar(300),
#dateList nvarchar(300),
#Month nvarchar(5),
#Year nvarchar(5),
#SearchType nvarchar(10)
AS
BEGIN
SET NOCOUNT ON;
declare #tempTableSelect nvarchar(2000)
DECLARE #tempattend Table
(
[Emp.ID] nvarchar(10),
[Name] nvarchar(60),
[1] nvarchar(3) null,
[2] nvarchar(3) null,
[3] nvarchar(3) null
upto ..............
[31] nvarchar(3) null
)
IF (#SearchType = 1)
BEGIN
--INSERT TEAM LIST TO #tempattend TABLE
insert into #tempattend([Emp.ID],[Name]) (Select EMP.empID as [Emp.ID],CONCAT(EMP.emp_fname,' ',COALESCE(nullif(EMP.emp_Mname,'') +' ',''),COALESCE(nullif(EMP.emp_Lname,'') +' ','')) as [Name] from EShiftHistory)
set #tempTableSelect = 'select [Emp.ID],Name,' + #dateList +' from #tempattend'
EXEC (#tempTableSelect)
END
END
You should write
set #tempTableSelect = 'select [Emp.ID],Name,' + #dateList +' from #tempattend'
#tempattend is a temporary table variable. It holds a table, not some value like #datelist.
But why do you EXEC instead of just selecting directly from the table?
Come to think of it: It may not be possible to use memory temp tables in EXEC statements. Try turning this
DECLARE #tempattend Table
into
CREATE TABLE #tempattend
and change every occurance of #tempattend to #tempattend.
Following up on Thorsten Dittmar's answer, why not something like this?
select empID,
CONCAT(emp_fname,' ',
COALESCE(emp_Mname,''),
COALESCE(nullif(emp_Lname,''))) as [Name]
from EShiftHistory
I realize you're trying to do some dynamic selection with the #dateList variable, but that's a strong signal that you should either normalize your tables or just let your client code pick out which columns it wants. (Not everything needs to be done in raw SQL.)
Numbers as column names? Definitely problematic.
Related
i have this stored procedure, I want to dynamically select table name based on the variable passing ie #Practice_Short_Name
Create procedure [dbo].[GetCompleteCPTDetails]
#Practice_Short_Name varchar(50) is Null,
#Uploaded_Date varchar(30) is Null
as
begin
DECLARE #CPTtablename varchar(100)
DECLARE #vQuery NVARCHAR(100)
--Dynamically select Table name based on #practice_short_name
set #CPTtablename ='ACER_CLAIMS_MASTER_DETAIL_Hist_'+#Practice_Short_Name+''
SET #vQuery = 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Number,Patient_First_Name,
Patient_Last_Name,Voucher_Number,Procedure_Code,Service_Fees,
Service_Payments,Service_Adjustments,Acer_Status,Acer_Allowed_Amount
from '+#CPTtablename+'
where Uploaded_Date ='+#Uploaded_Date+' and
Practice_Short_Name ='+#Practice_Short_Name+'
order by acer_status asc, Service_Date_From desc, Patient_First_Name asc'
EXEC #vQuery
end
GO
but while running this proc it is throwing error like
"Could not find stored procedure 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Numb'."
can anyone explains me what i am doing wrong..
This way you call a procedure
EXEC #vQuery
but this way you run dynamic sql
EXEC (#vQuery)
so your SP should look as below
Create procedure [dbo].[GetCompleteCPTDetails]
#Practice_Short_Name varchar(50) is Null,
#Uploaded_Date varchar(30) is Null
as
begin
DECLARE #CPTtablename varchar(100)
DECLARE #vQuery NVARCHAR(100)
--Dynamically select Table name based on #practice_short_name
set #CPTtablename ='ACER_CLAIMS_MASTER_DETAIL_Hist_'+#Practice_Short_Name+''
SET #vQuery = 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Number,Patient_First_Name,
Patient_Last_Name,Voucher_Number,Procedure_Code,Service_Fees,
Service_Payments,Service_Adjustments,Acer_Status,Acer_Allowed_Amount
from '+#CPTtablename+'
where Uploaded_Date ='+#Uploaded_Date+' and
Practice_Short_Name ='+#Practice_Short_Name+'
order by acer_status asc, Service_Date_From desc, Patient_First_Name asc'
EXEC (#vQuery)
end
GO
The variable you use to store the query is too short to store the entire query text.
This means that the query is truncated to fix the 100 characters limit of the #vQuery variable.
Use nvarchar(max) instead of nvarchar(100).
Also, using Dynamic SQL is usually a security hazard, leaving an opening to SQL Injection attacks. You might want to re-think your design and keep all the data in the same table, instead of keeping different tables with the same structure on your datadase.
2 things need to be corrected : length of your dynamic query variable that caused you this problem. use parameterized dynamic query to prevent sql injection.
DECLARE #CPTtablename varchar(100)
DECLARE #vQuery NVARCHAR(2000) -- increased length, you can also use nvarchar(max)
--Dynamically select Table name based on #practice_short_name
SET #CPTtablename ='ACER_CLAIMS_MASTER_DETAIL_Hist_' + #Practice_Short_Name + ''
SET #vQuery = 'select Practice_Short_Name,Service_Date_From,Carrier_Name,
Location_Description,Patient_Number,Patient_First_Name,
Patient_Last_Name,Voucher_Number,Procedure_Code,Service_Fees,
Service_Payments,Service_Adjustments,Acer_Status,Acer_Allowed_Amount
from ' + #CPTtablename + '
where Uploaded_Date = #Uploaded_Date and
Practice_Short_Name = #Practice_Short_Name
order by acer_status asc, Service_Date_From desc, Patient_First_Name asc'
--dynamic query with input params
EXEC sp_executesql
#vQuery,
N'#Uploaded_Date varchar(30), #Practice_Short_Name varchar(50)',
#Uploaded_Date = #Uploaded_Date,
#Practice_Short_Name = #Practice_Short_Name
Thank you for helping me out through my previous tides.. I am currently working on SQL Server 2008 for one of my project, a part of which needs to use 22 columns for a set of similar operations.
The column names only differ by the number, e.g.
C1_1,C1_2,c1_3
Is there any way to loop through the column names? I tried out the following code, but it throws out an error
DECLARE #i INT
SET #i=2
while (#i<=22)
begin
SELECT [DEF].[CONCATENATE], SUM(DEF.[C1_+#i+_PREV]) as
[C1_#i_prev]
INTO #TMP_C1_#i_CONCATENATE_PREV
FROM DEF
GROUP BY DEF.[CONCATENATE]
SELECT [ABC].[CONCATENATE], SUM(ABC.[C1_#i_CURR]) as
[c1_#i_curr]
INTO #TMP_C1_#i_CONCATENATE_CURR
FROM ABC
GROUP BY ABC.[CONCATENATE]
UPDATE #tmp_var_concatenate_c1_#i
SET [Amount] = #TMP_C1_#i_CONCATENATE_PREV.[C1_#i_PREV]
FROM #tmp_var_concatenate_c1_#i
INNER JOIN
#TMP_C1_#i_CONCATENATE_PREV ON
#tmp_var_concatenate_c1_#i.[CONCATENATE] = #TMP_C1_#i_CONCATENATE_PREV.
[CONCATENATE]
Please forgive my ignorance, if I am doing something idiotic.
Thanks
This is part of the code in which the code is burping.
alter table #tmp_var_concatenate_C1_'+cast(#i as varchar)+'
add [ColA] varchar(255),
[ColB] Varchar(255),
[ColC] Varchar(255),
[ColD] VARCHAR(50),
[ColE] MONEY,
[ColF] MONEY
Is it because of the #tables that I am using ?? but, ideally, it shouldnt be an issue whether am using a Temp table or a reg. one..
You can use dynamic sql:
DECLARE #SQL varchar(max), #i INT
SET #i=2
while (#i<=22)
begin
/* Then cover all calculations with this one: */
SET #SQL='SELECT [DEF].[CONCATENATE], SUM(DEF.[C1_'+cast(#i as varchar)+'_PREV]) as
[C1_'+cast(#i as varchar)+'_prev]
INTO #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV
FROM DEF
GROUP BY DEF.[CONCATENATE]
/* and all your code with the same trick in #i to the END */
'
--PRINT (#SQL) -- print it before use to see the result script
EXEC (#SQL)
/* Than do your iterations etc. */
set #i+=1
end
And don't forget to substitute all ' inside #SQL with ''.
Also you need to do all manipulations with temp tables inside #SQL, if you want to do final update outside the dynamic sql, just make tables real and then delete them.
[UPDATE]
As far as you faced with problem of altering temp tables, I tried to reproduce this error, but nothing happens, everything works fine. Please use this code as an example.
declare #sql varchar(max),#i int
set #i=2
while #i<=22
begin
set #sql='
select ID,Code into #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV from (select 0 as ID, ''a'' as Code) t1
alter table #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV add [Col1] varchar(255), [Col2] Varchar(255), [Col3] Money
select * from #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV'
--print (#sql)
exec (#sql)
set #i+=1
end
First, I create temp table with dynamic name. Second, add new columns. The last is successful verifying. Did you execute all creations/alters in the same #sql-batch? If no, this won't work, because this tables are available only inside this batch (that's why we used varchar(max) when declared). Please describe your actions in details, maybe there is a mistake somewhere.
What's a good way to dynamically create tables, then join together all the tables that I have created?
Background
I'm trying to write a stored procedure that collects data from various parts of the database and presents it for use. For example if I wanted to send invoices to both Alice and Bob, this stored procedure would find all the information for the invoice. The trick is that Alice and Bob don't always need all the information I have. If Bob is tax exempt but trades in simoleons, I'll need to include information about currency exchange rates but leave out tax info. I want to give the stored procedure an order number and get back results formatted to the customer.
My solution so far is to create a table holding customer requirements. Then my stored procedure checks the table: if currency data is required I create and populate #Currency, or #Taxes holds tax data and so on. I check the requirement, then create the table as needed. At the end I need to Join the data into one record set.
My problem is that I have a error saying
Incorrect syntax near the keyword 'table'
AND an error saying
Must declare the table variable #Summary
on line 1 where i define #Summary These errors only show up when I use dynamic SQL to join the tables. When I comment out the dynamic SQL and copy-paste the statements into the one it should create, I have the result I want.
Code
I tried to use the following dynamic SQL in Microsoft SQL Server Management Studio
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Create table
DECLARE #<TableName> table
(
ID varchar(20)
, <Stuff>
)
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Pepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #Summary, #LineItems, #Taxes, #Currency
Question
Is there something wrong with my code? Is there a better way to do this? The other option I though of was to have IF Exists trees with the entire JOIN statement at the bottom of each branch (Since I can't seem to interrupt the JOIN clause with IF's). The problem with that is that I'll need 2^n JOIN statements to join n possible tables. n in this case might be as high has 20 or 30.
I think I see a problem in the code (maybe 3 problems -- see "questionable" 1 & 2 below) --
1) [Changed on 10/21, after OP's comments] The big problem: table parameters passed in the final "EXECUTE sp_executesql #SQLQuery..." are sometimes not declared.
1a) #Summary is actually never declared... you declared and set #SummaryTable, then use #Summary.
Just change that to #SummaryTable (I did that in (4) below), and I think that will prevent your second error message ("Must declare the table variable #Summary").
1b) All the other tables are sometimes declared: each of their DECLARE statements are within an "IF EXISTS". I suggest either (I) make the declares unconditional (outide IF EXISTS), but still conditionally INSERT... or (II) make the format of the EXECUTE command vary with what's available.
(I doubt the unused table variables need anything in them....)
In point 4 below (added 10/21), I give an example that I have NOT TESTED with my own databases (that test would take a lot more time)... so please let me know how it goes...
2) [questionable 1] Simple case of mixed case ;) Note that the line...
Set #SQLQuery = #SqlQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
... first has an uppercase "SQL", then mixed-case "Sql".
Why I said this is "questionable" -- if your server's COLLATION is case-insensitive, what you typed above would be fine.
3) [questionable 2] You have both '#TableName' and '#Table Name' (with a space). I realize that may just be how you typed it, in posting your question.
Point (4), added in update to answer -- possible code
create procedure <procedure> (#OrderNumber varchar(20)) AS
DECLARE #CustomerName varchar(35) -- retrieved based on #OrderNumber
DECLARE #SQLQuery nvarchar(500)
DECLARE #ParamDef nvarchar(500)
DECLARE #SummaryTable table
(
ID varchar(20) --=#OrderNumber
, <Stuff>
)
SET #SQLQuery = 'Select * From #SummaryTable'
SET #ParamDef = '#SummaryTable table'
--Create table variables, though they may not be populated
DECLARE #LineItems
(
ID varchar(20)
, <Stuff>
)
DECLARE #Taxes
(
ID varchar(20)
, <Stuff>
)
DECLARE #Currencytable
(
ID varchar(20)
, <Stuff>
)
IF EXISTS(Select <TableName> from CustRequirements where <TableName> = 1 AND Customer = #CustomerName)
BEGIN
--Populate
Insert into <TableName> Select <RelevantData> From <DataSource> where <Condition based on #OrderNumber or #CustomerName>
--Prepare Dynamic SQL
Set #SQLQuery = #SQLQuery + ' FULL OUTER JOIN #<TableName> ON <TableName>.ID = SummaryTable.ID'
SET #ParamDef = ', #<TableName> table'
END
<repeat for other tables>
EXECUTE sp_executesql #SQLQuery, #ParamDef, #SummaryTable, #LineItems, #Taxes, #Currency
The solution I've decided tp use is to create a table then use ALTER TABLE to add columns as needed.
CREATE Table #Output
(
InvoiceNumber varchar(20)
, <stuff>
)
IF EXISTS(Select <ThisSegment> FROM CustRequirements WHERE <ThisSegment> = 1 AND Customer = #CustomerName)
BEGIN
ALTER TABLE #Output ADD <ThisSegment> <dataType>
UPDATE #Output
SET <ThisSegment> = <data> from <DataSource>
WHERE <InvoiceNumber = DataSource.invoice> AND <Other conditions>
END
<Repeat as needed>
I want to create a temporary table, in which the columns will be those which I provide as parameter, separated by a delimiter.
For example, if the column names are: id, name, address..the respective table should contain the same amount and header names of the columns. Similarly, next time the column number and names could vary.
Any help in this regard?
Try this :-
CREATE PROCEDURE GenerateTempTable
#tableName as nvarchar(max),
#Col1 as nvarchar(255),
#Col2 as nvarchar(255)
AS
BEGIN
Declare #sql nvarchar(max)
set #sql='CREATE TABLE #'+ #tableName + '
('+ #col1+ ' nvarchar(255),'+
#col2 + ' nvarchar(255)
)'
-- Select #sql Check the DDL
EXECUTE sp_executesql #sql,
N'#tableName nvarchar(max),#Col1 nvarchar(255),#Col2 nvarchar(255)',
#tableName = #tableName,#Col1=#Col1,#Col2=#Col2
END
The problem with the above query is temp table is created with the dynamic block query therefore it cannot be accessed after the block . In order to access the table outside the scope then you need to create global temp table ##
Edit :-
An example with Global Temp Tables and static table name
ALTER PROCEDURE GenerateTable
#Col1 as nvarchar(255),
#Col2 as nvarchar(255)
AS
BEGIN
Declare #sql nvarchar(max)
If object_id('tempdb..##TempTable') is not null
Drop table ##TempTable
set #sql='CREATE TABLE ##TempTable
('+ #col1+ ' nvarchar(255),'+
#col2 + ' nvarchar(255)
)'
-- Select #sql Check the DDL
EXECUTE sp_executesql #sql,
N'#Col1 nvarchar(255),#Col2 nvarchar(255)',
#Col1=#Col1,#Col2=#Col2
END
To execute the SP the sql is :-
Declare #tableName varchar(max),
#Col1 varchar(70),
#Col2 varchar(70)
Exec GenerateTable #col1='ColA',#Col2='ColB'
Edit 2:-
If you are sure that the number of parameters wont exceed x values ( Say 5) .Then you can create 5 default parameter .Check this link for further details.
Could you not build a table out of a distinct list from wherever these "Dynamic Field Names" live... Then push that in as a string list... Like... I built a table with colors then got a field of names and now am going to push it into a string that can be used to build out the table headers... no limit to quantity...
SELECT #Fields = coalesce(#Fields + ',', '') + convert(varchar(50),[name])
FROM #TempCols
WHERE column_id > 1
ORDER BY column_id
Where Column_ID is just a Windowed ROW_Number...
I don't agree with the notion of its not possible ever. There is always a way, we may not see it now but there is always a method that can be nested or abused to bend any rule to what we need.
My trigger is as below ,
Alter TRIGGER [dbo].[LogTable_InsertTrigger] on [dbo].[std_table] AFTER INSERT
as
DECLARE #ColName varchar(50), #QueryText nvarchar(max)
declare #inserted TABLE(
[CountryID] [int] NOT NULL,
[Country] [nvarchar](255) NOT NULL,
[RegionId] [int] NULL
)
insert into #inserted
select * from inserted
DECLARE objCursor CURSOR FAST_FORWARD FOR
select ColName from dbo.getColumnNames('std_table')
OPEN objCursor
FETCH NEXT FROM objCursor INTO #ColName
WHILE ##FETCH_STATUS = 0
BEGIN
set #QueryText= '
insert into dbo.LogTable
(StandardType,Attribute,Action,NEwValue,UserId,ModifiedDate)
select ''Country'','''+#ColName+''',''Insert'','+#ColName+',1,getdate()
from #inserted'
EXEC sp_executesql #QueryText
FETCH NEXT FROM objCursor INTO #ColName
END
CLOSE objCursor
DEALLOCATE objCursor
When I try to insert to table std_table in DA Layer I get the exception Must declare the table variable "#inserted".
I couldn't use the inserted table directly because I am writing a dynamic query, inside which magic tables wont work. So I am trying to dump the data in inserted table to a temp table and to access from that.
I tried with
select *
into #inserted
from inserted
This works, but since my application is accessed by many users over network this will cause data issues. So I can't use this.
Several things wrong here.
That is a table variable, not a user defined table type.
If #temp tables work, why do you think that will cause data issues for multiple users? Each user will get their own version of the #temp table.
If you know there are exactly three columns and you can hard-code the table variable declaration, why do you need to then generate the three sets of inserts dynamically? Aren't the column names CountryID,Country,RegionID?
If you really need to do this dynamically then it seems like you could do this an easier way without an explicit cursor - not that this is necessarily a bad thing or that it will perform any worse than the below, but the cursor is just much more verbose and ugly code:
ALTER TRIGGER [dbo].[LogTable_InsertTrigger]
ON [dbo].[std_table]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
SELECT * INTO #t FROM inserted;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'';
SELECT #sql = #sql + CHAR(13) + CHAR(10) + N'INSERT INTO dbo.LogTable
(StandardType,Attribute,Action,NewValue,UserId,ModifiedDate)
SELECT ''Country'','''+ColName+''',''Insert'','+ColName+',1,GETDATE()
FROM #t;'
FROM dbo.GetColumnNames('std_table');
EXEC sp_executesql #sql;
END
GO