I'm trying to figure out why my where clause is returning all rows.
I'm querying a column that contains csv's using a variable that also contains csv's. I've built a stored function to split the variable on csv and return a table with one row that contains what I'd like to have on the right side of the LIKE operator.
Example:
The stored function:
ALTER Function [dbo].[storedFunction]
(#Fields VARCHAR(MAX),
#Field_Name VARCHAR(MAX) = '')
RETURN #Tbl_Fields Table (FIELD Varchar(max))
AS
BEGIN
DECLARE #FIELD varchar(max) = REPLACE(#Fields, ',', '%''' + ' AND ' +
#Field_Name + ' Like ' + '''%');
INSERT INTO #Tbl_Fields
SELECT '''%' + #FIELD + '%'''
RETURN
END
Using the stored function:
BEGIN
DECLARE #variable varchar(max) = 'variable1, variable3';
END
SELECT field
FROM storedFunction(#variable, 'main_csv_field');
returns '%variable1%' AND main_csv_field Like '%variable3%'
My simplified query:
BEGIN
DECLARE #variable varchar(max) = 'variable1, variable3';
END
SELECT main_csv_field
FROM table
WHERE (main_csv_field LIKE (SELECT field
FROM storedFunction(#variable, 'main_csv_field');
returns
variable1,variable2,variable3,variable4,...
variable2,variable4,...
variable1,variable3,...
My problem is this last query returns all of the rows in the table regardless of value matching. Were I to copy and paste the value returned from the stored function I would get the data that I need.
How/what is the difference here?
Thanks to #Obie and #AllanS.Hansen I knew where to start looking to fix this. Its pretty rough, but I wanted to post a solution before I got too far down the rabbit hole:
DECLARE variable1 varchar(max) = '' --around 9 passed from code
DECLARE #query nvarchar(max);
DECLARE #column_list varchar(max) = 'column1, column2, etc'
--one of each of the tests per variable passed from code
DECLARE #variable1_test nvarchar(max) = (SELECT CASE WHEN #variable = '' THEN '%' ELSE (SELECT * from dbo.stored_function(#variable, 'column_name')) END);
END;
SET #query = ' SELECT ' + #column_list + '
FROM table_name
WHERE variable LIKE ''' + #variable_test + ''' '
EXECUTE sp_executesql #query;
print(#query); --This is just to see the resulting query, which helped me a ton
Exciting! Now I have to test it.
Related
I am trying to dynamically insert data into a temp table passing like data as a variable:
DECLARE #data NVARCHAR(MAX)
SET #data = 'INSERT INTO #coco ' + '([' + #val + '])' + ' SELECT [USER_ID] FROM [dbo].[Sheet1$] WHERE [Standard_Name] LIKE ' + #val
EXEC sp_executesql #data
#val is a column name selected from table Sheet1$ and few column name has space between them. While executing, I am getting error, like for column name "Acrobat Reader":
Incorrect syntax near 'Acrobat'.
Also if I am adding data using hardcoded one by one in a column its adding data to one column while other column its adding NULL.
Any suggestion how I can overcome this?
Parametrise your SQL, and this problem "goes away":
DECLARE #data;
SET #data = N'INSERT INTO #Coco (' QUOTENAME(#val) + N')' + NCHAR(10) +
N'SELECT [USER_ID]' + NCHAR(10) +
N'FROM dbo.[Sheet1$]' + NCHAR(10) +
N'WHERE [Standard_Name] = #val;'; --As this doesn't contain a %, there's no need for LIKE
EXEC sp_executesql #data,
N'#val = sysname', --guessed datatype
#val = #val;
Note the comments I made in the SQL though.
Afraid, I've no idea what your second statement means. You'll need to explain further.
I have 6 table with different fields. I want to access table name dynamically. is there any idea to do it?
My code is below this is simple procedure which I want to make dynamic to use in c#. how to do it?
Create procedure [dbo].[Insert_Data] (#Id int,#FeesHead nchar(20),#Fees int,#Remarks nchar(20))
as
begin
Insert into FeesHead(ID,FeesHead,Fees,Remarks)values(#Id,#FeesHead,#Fees,#Remarks)
End
Don't go there.
It's a bad idea since you will end up with a long, inefficient stored procedure that will be vulnerable to SQL injection attacks and have performance issues.
Writing an insert stored procedure for each table is the way to go.
You wrote you have six different tables with different columns, so writing a stored procedure to handle inserts for all of them will require you to send all the parameters for all columns as well as a parameter for the table name, and a nested if...else with 6 possible paths, one for each table.
This will end up as a long, messy, poorly written code at best, bad in each parameter: security, performance, code readability and maintainability.
The only way that makes some sense to achieve such a goal is to write individual insert stored procedures for each table, and then write a stored procedure that will take all of the possible parameters and the table name and inside of it decide what insert stored procedure to execute based on the value of the table name parameter. However, you will be better off leaving conditions like these to the SQL client (your c# code in this case) then to SQL Server.
Its very easy to do.........
Just call the sql query using Data Adapter.
select TABLE_NAME from INFORMATION_SCHEMA.TABLES
As you said you need dynamic SQL like this:
Create procedure [dbo].[Insert_Data]
(
#TableName nvarchar(512),
#Values nvarchar(max)
)
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'INSERT INTO ' + #TableName + ' VALUES (' + #Values + ')'
EXEC(#SQL)
END
Note that #Values will be like this '1, ''name'', 10.2' and with the same order of columns.
or
Create procedure [dbo].[Insert_Data]
(
#TableName nvarchar(512),
#Fields nvarchar(max),
#Values nvarchar(max)
)
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'INSERT INTO ' + #TableName + ' (' + #Fields + ') VALUES (' + #Values + ')'
EXEC(#SQL)
END
To more ability to handle column order and remove identity columns.
As Robert Harvey mentioned it is a bad idea, anyway if you want to you can do something like....
CREATE PROCEDURE Insert_Data
#TableName SYSNAME
,#Column1 SYSNAME = NULL
,#Column2 SYSNAME = NULL
,#Column3 SYSNAME = NULL
,#Value1 NVARCHAR(100) = NULL
,#Value2 NVARCHAR(100) = NULL
,#Value3 NVARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' INSERT INTO ' + QUOTENAME(#TableName)
+ N' ( '
+ STUFF(
CASE WHEN #Column1 IS NOT NULL
THEN N',' + QUOTENAME(#Column1) ELSE N'' END
+ CASE WHEN #Column2 IS NOT NULL
THEN N',' + QUOTENAME(#Column2) ELSE N'' END
+ CASE WHEN #Column3 IS NOT NULL
THEN N',' + QUOTENAME(#Column3) ELSE N'' END
,1,1,'')
+ N' ) '
+ N' VALUES ( '
+ STUFF(
CASE WHEN #Value1 IS NOT NULL
THEN N', #Value1' ELSE N'' END
+ CASE WHEN #Value2 IS NOT NULL
THEN N', #Value2' ELSE N'' END
+ CASE WHEN #Value3 IS NOT NULL
THEN N', #Value3' ELSE N'' END
,1,1,'')
+ N' ) '
Exec sp_executesql #Sql
,N'#Value1 NVARCHAR(100),#Value2 NVARCHAR(100),#Value3 NVARCHAR(100)'
,#Value1
,#Value2
,#Value3
END
Say i have columns PlanSalesMt1, PlanSalesMt2 and so on for the whole year until Mt12 in a table.
Before i created a view where i unioned all the months togheter but that query is pretty slow. Now i want to try the query with a Loop.
My Problem is i can't use the variable in my column names because they get invalid. Is that a way to solve this?
What i mean is i want to loop my question 12 times and change the name of the columns also after what month it is. But i cant make a #i variable and say select PlanSalesMt#1 or PlanSalesMt+#1 or PlanSalesMt+CAST(#1 as varchar) becuase it says invalid column Name.
Here is an example of dynamic SQL
DECLARE #ColumnName1 VarChar(20)
DECLARE #ColumnName2 VarChar(20)
DECLARE #sql Varchar(1000)
DECLARE #MyWhere VarChar(20)
SET #ColumnName1 = 'PlanSalesMt1'
SET #ColumnName2 = 'PlanSalesMt2'
SET #MyWhere = 'Books'
SET #sql = 'SELECT ' + #ColumnName1 + ', ' + #ColumnName2 + ' FROM TABLE WHERE ' + #ColumnName1 + ' = ''' + #MyWhere + ''''
EXEC(#sql)
If you change EXEC(#sql) to SELECT #sql copy into SSMS and run it you will see the query this produces.
I need to create about 300 columns for a table and I don't want to to it manually.
How can I do this?
I want to have columns like
Bigint1..to..Bigint60
Text1 ..to..Text60
and so on.
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'mytbl'))
begin
create table OBJ_AttributeValues(
ObjectID numeric(18,0) not null
);
end
else
begin
DECLARE #A INT
set #A = 1;
WHILE(#A <=60)
BEGIN
alter table OBJ_AttributeValues
add ...............................
set #A = #A+1;
END
end
What should I write instead of "..."?
You will need to use dynamic SQL for that, something like
DECLARE #SSQL VARCHAR(1000)
DECLARE #A INT
set #A = 1;
WHILE(#A <=60)
BEGIN
SET #SSQL = 'alter table OBJ_AttributeValues add Bigint' + CAST(#A as varchar) + ' bigint'
EXEC (#SSQL)
set #A = #A+1;
END
This isn't really a good idea, you should take the time to write the sql or just copy-paste the columns from Excel or something like that. You also shouldn't be using the TEXT data type, is deprecated and filled with restriction (use VARCHAR(MAX) instead if you need). That said, here is a way using dynamic SQL:
DECLARE #BigintCols NVARCHAR(MAX), #TextCols NVARCHAR(MAX)
DECLARE #Query NVARCHAR(MAX)
SET #BigintCols = ''
SET #TextCols = ''
SELECT #BigintCols = #BigintCols + N'Bigint' + CAST(number AS NVARCHAR(2)) + N' BIGINT,',
#TextCols = #TextCols + N'Text' + CAST(number AS NVARCHAR(2)) + N' TEXT,'
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 60
ORDER BY number
SET #Query = '
CREATE TABLE OBJ_AttributeValues(ObjectID numeric(18,0) not null,'+#BigintCols+
LEFT(#TextCols,LEN(#TextCols)-1)+')'
EXECUTE sp_executesql #Query
Oh, you should probably read about dynamic sql first.
I'm trying to execute an inline SQL statement within a stored procedure. I'm working with SQL Server 2008.
The problem is that I can't execute the first inline statement (with WHERE clause). It crashes because the string within EXEC(...) is dynamically created and all concatenated variables must be of type varchar.
Error that appears when calling procedure:
An expression of non-boolean type specified in a context where a
condition is expected, near 'ORDER'.
The procedure looks like:
CREATE PROCEDURE loadMyRows
#table_name nvarchar(50),
#bounding_box varchar(8000)
AS
BEGIN
-- *********************************** COMMENT *********************************
-- ** This two code lines are correct and will return true (1) or false (0), **
-- ** but they doesn't work within inline EXEC(...) **
--DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
--select TOP(5) wkt.STWithin(#bb) AS 'bool'
-- *********************************** COMMENT *********************************
IF #bounding_box <> ''
BEGIN
DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
EXEC(
'SELECT TOP (' + #row_limit + ') * ' +
'FROM ' + #real_table_name + ' ' +
'WHERE wkt.STWithin('+#bb+') ' + -- <-- doesn't work :-(
-- 'WHERE wkt.STWithin(geometry::STGeomFromText('''+#bounding_box+''', 4326)) ' +
-- ^^ doesn't work, too :-(
'ORDER BY id ASC '
);
END
ELSE
BEGIN
EXEC(
'SELECT TOP (' + #row_limit + ') * ' +
'FROM ' + #real_table_name + ' ' +
'ORDER BY id ASC'
);
END
END
I've found a working solution for this problem. The way the MSDN showed me was http://msdn.microsoft.com/en-US/library/ms175170.aspx. There's written:
[...] the string is executed as its own self-contained batch.
That let me know, if I want to execute a dynamic statement with a table variable as string, it's the same as I would execute the query without the EXECUTE command, like:
SELECT TOP(#row_limit) *
FROM #real_table_name
WHERE ...
ORDER BY id ASC;
And this would probably not work for the table name.
So, if I write instead:
DECLARE #sql_statement nvarchar(MAX) = 'SELECT TOP(#limit) *
FROM ' + #real_table_name + '
ORDER BY id ASC';
-- declaration of parameters for above sql
DECLARE #sql_param_def nvarchar(MAX) = '#limit int';
EXECUTE sp_executesql #sql_statement, #sql_param_def, #limit = #row_limit;
Then, this would work. This is because I define the #sql_statement simply as a concatenated string which will just resolve the dynamic table name at runtime to a string with the name of the real existing table. The #limit parameter is untouched and is still a parameter.
If we then execute the batch we only must pass a value for the #limit parameter and it works!
For the geometry parameter it works in the same way:
DECLARE #bb geometry = geometry::STGeomFromText(#bounding_box, 4326);
SET #sql_statement = 'SELECT TOP(#limit) *
FROM ' + #real_table_name + '
WHERE wkt.STWithin(#geobb) = 1
ORDER BY id ASC';
-- NOTE: This ' = 1' must be set to avoid my above described error (STWithin doesn't return a BOOLEAN!!)
-- declaration of parameters for above sql
SET #sql_param_def = '#limit int, #geobb geometry';
EXECUTE sp_executesql #sql_statement, #sql_param_def, #limit = #row_limit, #geobb = #bb;
Hope this was clear ;-)
create proc usp_insert_Proc_Into_temp
#tempTable nvarchar(10) output
as
begin
set #tempTable = '##temp'
declare #query nvarchar(200)
--Select statement
set #query = 'select 1 as A,2 as B, 3 as C into'+ ' '+#tempTable+''
exec(#query)
end
go
declare #tempTable nvarchar(10)
exec usp_insert_Proc_Into_temp #tempTable output
exec('select * from' + ' '+ #tempTable+'')
exec ('drop table'+ ' '+#tempTable+'')