I want to create a table with column names like Members_2014 and Spend_2014. I've tried to have the year setup as a variable and concatenate Member_ + #year, so that the #year will decrease, but the code I've written doesn't seem to work:
declare #li_year int
declare #li_year2 int
declare #li_year3 int
declare #li_year4 int
declare #li_year5 int
set #li_year = datepart(year, GETDATE())
set #li_year2 = #li_year-1
set #li_year3 = #li_year-2
set #li_year4 = #li_year-3
set #li_year5 = #li_year-4
drop table [scratchdb].[dbo].results_test
create table [scratchdb].[dbo].results_test (RDesc varchar(30),
'Members_' + #li_year int,
'Spend_' + #li_year money,
'Members_' + #li_year2 int,
'Spend_' + #li_year2 money,
'Members_' + #li_year3 int,
'Spend_' + #li_year3 money,
'Members_' + #li_year4 int,
'Spend_' + #li_year4 money,
'Members_' + #li_year5 int,
'Spend_' + #li_year5 money)
I'd like to start with a personal observation: This is quite likely a bad idea in the long run. Ideally, database schemas are fixed. Your script, however, would produce a different database schema depending on the year you're running it it (if it worked, that is). Which means, for example:
You won't be able to use your script to re-create a schema that used to work one, two years earlier.
Your will have to rewrite all queries and applications that run against this database every year.
So in the short run, what you're doing might be OK, but it would be better to simply normalize your schema and make the year an additional column:
CREATE TABLE dbo.results_test
(
RDesc VARCHAR(30),
Year SMALLINT NOT NULL UNIQUE, -- UNIQUE to enforce at most one record per year
Members INT,
Spend MONEY
);
If you decide against this and in favour of your current solution, then you need to put the whole SQL statement in a string variable, not just parts of it:
DECLARE #sql NVARCHAR(MAX) =
'CREATE TABLE … Members_' + CAST(#li_year AS NVARCHAR) + ' INT, …' + …
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
-- regular string concatenation
EXECUTE sp_executesql #sql;
SQL statements contained in a string variable are called Dynamic SQL, and the sp_executesql system stored procedure helps you run such a statement.
Related
I've different different tables to categorically store data and a log table where all the transactions log are recorded
e.g. 1) VoucherNO, Add, ...
2) VoucherNO, Delete, ..
After I backup the database and restore in another server for my Reporting Purpose. That time I want to ensure all the log data and transaction are available in TestDB if not then I remove log from 'AUD_USER_ACTIVITY'.
To find the transaction exist or not, I create a dynamic sql select statement and check whether record is exist or not.
Basis on #RecExist Value I do the action like if records is not available in TestDB the log will be remove, if record exist immediately break this loop and going for next procedure
But #RecExist variable is not updating in Dynamic SQL Execution. Please guide me
declare #MvDocNo varchar(50)
DECLARE #SCtr as DECIMAL(10,0)
declare #LocationCode varchar(4)
declare #UName Nvarchar(40)
declare #toe varchar(30)
declare #QryTxt as nvarchar(MAX);
Declare #RecExist as INT =0;
SET #RecExist=0
WHILE #RecExist=0
BEGIN
select top 1 #MvDocNo=DOCNO, #SCtr=SrlNo,#LocationCode =DMLTYPE,#UName=TABLENAME
FROM R_AUDDB..AUD_USER_ACTIVITY
WHERE DBNAME='TestDB' and DMLTYPE not in ('AD','D','PD') ORDER BY SRLNO DESC;
select top 1 #toe=docno from TestDB..M_TYPEOFENTRY where TBLNAME=#UName;
set #QryTxt='Select #RecExist=1 From R_TestDB..'+#UName+ ' Where '+#toe+'='''+#MvDocNo+''''
exec (#QryTxt)
IF #RecExist=0
BEGIN
DELETE R_AUDDB..AUD_USER_ACTIVITY WHERE SRLNO=#SCtr
END
END
The following code sample demonstrates how to check for a row in a table with a specific column and value using dynamic SQL. You ought to be able to change the values of the first three variables to reference a table and column in your database for testing.
Note that SQL injection is still possible: there is no validation of the table or column names.
-- Define the table to check and the target column name and value.
declare #TableName as SysName = 'Things';
declare #ColumnName as SysName = 'ThingName';
declare #TestValue as NVarChar(32) = 'Beth';
-- Create a SQL statement to check for a row in the target table with the specified column name and value.
declare #SQL as NVarChar(1024);
declare #Result as Bit;
-- Note that only object names are substituted into the statement at this point and QuoteName() is used to reduce problems.
set #SQL = N'select #iResult = case when exists ( select 42 from dbo.' + QuoteName( #TableName ) +
N' where ' + QuoteName( #ColumnName ) + N' = #iTestValue ) then 1 else 0 end;'
select #SQL as SQL;
-- Execute the SQL statement.
-- Note that parameters are used for all values, i.e. the target value and return value.
execute sp_executesql #stmt = #SQL,
#params = N'#iTestValue NVarChar(32), #iResult Bit output',
#iTestValue = #TestValue, #iResult = #Result output
-- Display the result.
select #Result as Result;
I need to delete (read/update) some values in some tables, and would like to use a stored procedure to reduce security issues.
Since tables are many and records even more and it is not reasonable to write a stored procedure for each combination, and since everybody has this kind of need, I thought could have been easy to find a stored procedure to do this.. but googling a lot I did not find a simple and short answer, so I tried to build my own stored procedure. But I'm afraid it could have some security issues: principally when I declare #Table as nvarchar(30).. I tried to declare as TABLE but it returns error..
Can suggest what is not acceptable and suggest a solution?
Thanks
Here the stored procedure for deleting.. but for other action could be similar:
CREATE PROCEDURE dbo.spDeleteRecord
(
#UID nvarchar(20) = NOT NULL,
#PWD nvarchar(30) = NOT NULL,
#Table sysname,
#WhereField sysname,
#WhereValue nvarchar(150) = NOT NULL
)
AS
SET NOCOUNT ON;
DECLARE #S nvarchar(max) = '',
#P nvarchar(max) = ''
SET #S = 'DELETE t
from dbo.'+quotename(#Table)+' t
join dbo.subUsers su on t.UID = su.UID
where ' + quotename(#WhereField) + ' = #_WhereValue
and su.SUID = #_UID
and su.PWD = #_PWD'
SET #P = '#_UID nvarchar(50),
#_PWD nvarchar(50),
#_Table sysname,
#_WhereField sysname,
#_WhereValue nvarchar(150)'
--PRINT #S
EXEC sp_executesql #S, #P, #UID, #PWD, #Table, #WhereField, #WhereValue
SET NOCOUNT OFF
Thanks for reading
Joe
You would need to do it like this:
SET #S = 'DELETE t
from dbo.'+quotename(#Table)+' t
join dbo.subUsers su on t.UID = su.UID
where ' + quotename(#WhereField) + ' = #_WhereValue
and su.SUID = #_UID
and su.PWD = #_PWD'
SET #P = '#_UID nvarchar(50),
#_PWD nvarchar(50),
#_WhereValue nvarchar(150)'
--PRINT #S
EXEC sp_executesql #S, #P, #_WhereValue = #WhereValue, #_UID = #UID, #_PWD = #PWD
Basically, the parameter list can only refer to parameters that are actually embedded in the SQL string.
Also, note that #Table and #WhereField would be more correct as datatype sysname. I would also probably have restricted #UID, #PWD, and #WhereValue to NOT NULL because I hate unhandled nulls.
However, you really need to consider if you want to do this. To me this feels like leaving a loaded gun lying around. What happens when you call this with a table that happens to have a UID field that happens to coincide to the values in the dbo.subUsers table even though no relation exists there? I don't see any significant advantage of this method over just running the parameterized query from your application, and the fact that the query changes between executions means that you may run into problems with parameter sniffing so you may end up with suboptimal execution plans.
Suppose the following:
CREATE PROCEDURE [MySPROC]
AS
BEGIN
CREATE TABLE #tempSubset(
[MyPrimaryKey] [bigint] NOT NULL,
[OtherColumn] [int] NOT NULL)
INSERT INTO #tempSubset (MyPrimaryKey, OtherColumn)
SELECT SomePrimaryKey, SomeColumn
FROM SomeHugeTable
WHERE LimitingCondition = true
SELECT MyPrimaryKey, OtherColumn
FROM #tempSubset
WHERE SomeExpensiveCondition = true
END
When I generate a function import or map a return type, EF doesn't generate a complex type or tells me:
The selected stored procedure or function returns no columns
How to overcome this?
Other answers suggest using table variables (not going to do this for performance reasons) faking the return schema and commenting out the real stored procedure, other suggest doing similar with views... but there must be a way to do this without having to add unnecessary overhead or requiring me to break a stored procedure to update the model?
CREATE PROCEDURE [MySPROC]
AS
BEGIN
--supplying a data contract
IF 1 = 2 BEGIN
SELECT
cast(null as bigint) as MyPrimaryKey,
cast(null as int) as OtherColumn
WHERE
1 = 2
END
CREATE TABLE #tempSubset(
[MyPrimaryKey] [bigint] NOT NULL,
[OtherColumn] [int] NOT NULL)
INSERT INTO #tempSubset (MyPrimaryKey, OtherColumn)
SELECT SomePrimaryKey, SomeColumn
FROM SomeHugeTable
WHERE LimitingCondition = true
SELECT MyPrimaryKey, OtherColumn
FROM #tempSubset
WHERE SomeExpensiveCondition = true
END
Supplying a faux data contract for the result set is the easiest, cleanest and fastest way to take care of the issue. This same problem exists in data source controls in SSIS too. .NET will read the result set from the unreachable "contract" section of the query and supply the metadata for the complex type. No performance impact and no need to comment out the SQL that does the actual work.
Adding this to the top of the stored procedure definition: SET FMTONLY OFF allowed the model to infer the schema from the temporary table without issue. As a bonus, it doesn't require additional maintenance for a contract.
Example:
SET FMTONLY OFF
CREATE TABLE #tempTable (
...
)
...
SELECT * FROM #tempTable
Solution 1
Use a table variable instead of a temporary table.
Solution 2
Use the Set FMTONLY off; SQL command in the procedure and you will get the column information to create a new complex type.
Solution 3
This is not a good way, but it's a very easy way. Just add a select statement with dummy data and it will not execute because 1=0.
you can check details on this link
This is incomplete but when set fmtonly off does not work, you can generate the data contract using the following:
SELECT *
FROM tempdb.sys.columns
WHERE [object_id] = OBJECT_ID(N'tempdb..#u');
select case system_type_id
when 62 then 'cast(null as float) as '
when 175 then 'cast(null as char(' + cast(max_length as varchar(50)) + ')) as '
when 167 then 'cast(null as varchar(' + cast(max_length as varchar(50)) + ')) as '
when 56 then 'cast(null as int) as '
when 104 then 'cast(null as bit) as '
when 106 then 'cast(null as decimal(' + cast(precision as varchar(50)) + ',' + cast(scale as varchar(50)) + ')) as '
when 40 then 'cast(null as date) as '
end
+ name + ','
from tempdb.sys.columns
WHERE [object_id] = OBJECT_ID(N'tempdb..#u');
I'm receiving an error:
Conversion failed when converting the varchar value 'INSERT INTO TableRowCount (IntFieldID, DecimalField) SELECT 'to data type int"
Using the following code:
DECLARE #start INT -- #start is an INT
SET #start = 1 -- INT
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'INSERT INTO TableRowCount (IntFieldID, DecimalField)
SELECT ' + #start +', COUNT(*)
FROM dbo.somewhere' -- location is irrelevant
EXECUTE(#sql) -- this is where it fails
If I remove IntFieldID and the #start, it will work with an insert (though it defeats the purpose). I've tried including a SELECT CAST(' + #start + ' AS INT), which seems a little redundant since #start is an INT already (casting an INT as an INT), but that doesn't work either. I also tried beginning with an N' DYNAMIC-SQL, which didn't work, I tried using three ''' around everything (didnt' work), and in a few places that I read online, responses suggested putting the variable in the string, which generated the error:
Must declare scalar variable #start
(no surprise, as that didn't sound correct).
A better way than trying to concatenate an integer is to pass it in as a strongly-typed parameter:
DECLARE #start INT = 1;
DECLARE #sql NVARCHAR(MAX) = N'INSERT ...
SELECT #start, COUNT(*) FROM ' + #conn;
EXEC sp_executesql #sql, N'#start INT', #start;
You need to convert your #Start to a varchar.
DECLARE #sql NVARCHAR(MAX)
SET #sql = 'INSERT INTO TableRowCount (IntFieldID, DecimalField)
SELECT ' + CAST(#start as nvarchar(20)) +', COUNT(*)
FROM ' + #conn
SQL Server implicitly converts between datatypes on concatenation or addition based on some fairly complex criteria. Suffice to say if you try to combine an int and a string it will always attempt to convert the string to an int unless you tell it otherwise explicitly.
Below is a conversion chart for your reference from MSDN.
I have a variable which contains the following string: AL,CA,TN,VA,NY
I have no control over what I get in that variable (comes from reporting services)
I need to make it look like this: 'AL','CA','TN','VA','NY'
How do I do this?
declare #x varchar(50) = 'AL,CA,TN,VA,NY'
select '''' + REPLACE(#x, ',', ''',''') + ''''
I ended up doing something very similar that I thought I'd post. (I'll give credit to Mitch however)
This takes care of the middle:
SET #StateList = REPLACE(#StateList, ',', ''',''')
Then quote the edges:
SET #WhereClause1 = #WhereClause1 + 'AND customerState IN (''' + #StateList + ''') '
For a more generic answer, when you don't know what your output will look like exactly, use regular expressions.
This would let you you match on something like [A-Z]{2} and replace it with '$&'.
A commenter suggested this is overkill for this task - agreed, if you can guarantee you will always get a string like that. However, other people find these question pages later with similar, but not exact, problems, so other options are helpful to have.
Don't bother with dynamic sql.
You need to convert the string to a table
so
A,B,C,D
becomes
Value
A
B
C
D
using a function like
http://www.sqlusa.com/bestpractices/training/scripts/splitcommadelimited/
then you can use CROSS APPLY (which is like joining to a table, but a table created by a function) or you can just put it in a table variable and join to that
I want to know y does the following script run in SQL and not in T-SQL
DECLARE #tblName varchar(30)
SET #tblName = CONVERT(VARCHAR(20),GETDATE(),112) + 'Table'
DECLARE #sql nvarchar(4000)
SELECT #sql =
'CREATE TABLE "' + #tblName + '"
(
ID VARCHAR(15),
Name VARCHAR(15)
)'
EXEC(#sql)
go
it gives you the error
Msg 170, Sev 15: Line 1: Incorrect syntax near '20090714Table'. [SQLSTATE 42000]