I'm writiting a store procedure.
This procedure is getting 4 arguments which will be used in where clause. Problem is that these parameters can be empty.
I'm trying to write something like this:
select * from Books
If(#param1 <> "")
add where title =#param1
to the clause
But I have no idea how to make it. Is it even possible?
This type of query is called catch-all-query. There are several ways to do this, one of which is using combinations ofAND and OR conditions:
SELECT *
FROM Books
WHERE
(#param1 = '' OR title = #param1)
Another way is to use dynamic sql:
DECLARE #sql NVARCHAR(MAX) = ''
SELECT #sql = 'SELECT * FROM Books WHERE 1 = 1'
IF #param1 <> '' BEGIN
SELECT #sql = #sql + ' AND title = #param1'
END
EXEC sp_executesql
#sql,
N'#param1 VARCHAR(10)',
#param1 = #param1
Additional reading:
Erland Sommarskog's article on dynamic search conditions.
You can try like this
Select * from Books where (Title=#title or #title='')
select *
from Books
where (title = #param or #param = '')
If you want handle null in your parameter and want to include records where there is a null in the title field you can use
select *
from Books
where ISNULL(title,#)=ISNULL(#param,#)
Related
Creating a user-defined table-valued function which should return select union all dynamic query.
I have table tbl_tablesinfo which contains table names tbl1, tbl2, tbl3, etc. in all around 3000 table names.
I don't want to create view but function which should return select * from all tables by doing union all.
My attempt:
CREATE FUNCTION udf_alldata()
RETURNS TABLE
AS
BEGIN
DECLARE #Var VARCHAR(MAX) = ''
SELECT
#Var = STUFF((SELECT ' SELECT * FROM [' + tbl.TableNames + '] UNION ALL'
FROM [TestDB].SYS.TABLES tb
INNER JOIN [TestDB].dbo.[tbl_tablesinfo] tbl ON tb.name = tbl.TableNames
FOR XML PATH('')), 1, 1, '');
SET #var = LEFT(#var, LEN(#var) - 10);
EXEC #var
RETURN
END
I'm getting an error:
Incorrect syntax near 'BEGIN'.
Reason for doing this is creating view with 3k tables is getting slow and taking around 30 min of time, so I am looking for an alternative by creating function.
In the docs is clear said, that:
User-defined functions cannot make use of dynamic SQL or temp tables.
Table variables are allowed.
which means that you need to use a stored procedure and this is not bad as you can still insert the data in table if you want:
INSERT INTO #Table
EXEC [dbo].[stored_procedured_name]
INSERT INTO #Table
EXEC [dbo].[stored_procedured_name]
So, in your case you will have:
CREATE PROCEDURE udf_alldata
AS
BEGIN
DECLARE #Var VARCHAR(MAX) = ''
SELECT
#Var = STUFF((SELECT ' SELECT * FROM [' + tbl.TableNames + '] UNION ALL'
FROM [TestDB].SYS.TABLES tb
INNER JOIN [TestDB].dbo.[tbl_tablesinfo] tbl ON tb.name = tbl.TableNames
FOR XML PATH('')), 1, 1, '');
SET #var = LEFT(#var, LEN(#var) - 10);
EXEC sp_executesql #var
RETURN
END
Note, actually you can execute dynamic T-SQL in function but this is special case using SQL CLR. I can show you how to do this, but it will be better to stuck with the stored procedure.
You can't use Dynamic Query in SQL Function...
Functions can return only Scalar Values, or Tables...
Instead you can use Stored Procedure...
I have a database including a table called [Table] which lists all tables in that database. Now I want to write a SQL query using some JOINS, which gets a specific tablename from [Table] in a subquery to select from that table... I hope this is not too confusing.
So [Table] looks like this:
IdTable Tablename
1 Adress
2 Project
3 User
...
The query should look like this:
SELECT * FROM (SELECT Type FROM dbo.[Table] WHERE tablename = 12)
Would something like that be possible?
I know, that subqueries are possible, but I do not know how to do that in this case.
Thanks in advance.
Regards
Lars
Try this below dynamic code to get your expected result.
DECLARE #sqlCommand AS NVARCHAR(MAX)
DECLARE #TableName AS NVARCHAR(MAX)
SELECT #TableName = tablename FROM dbo.[Table] WHERE Type = 12
SET #sqlCommand= N'SELECT * FROM '+#TableName+''
EXEC (#sqlCommand)
Note: I guess you wants to select TableName where Type = 12 and I alter the selection and filtering accordingly.
You can also execute the query directly as below without creating the command string-
DECLARE #TableName AS NVARCHAR(MAX)
SELECT #TableName = tablename FROM dbo.[Table] WHERE Type = 12
EXEC (N'SELECT * FROM '+#TableName+'')
I'm trying to create a searchstring that is a bit dynamic, and i'm trying to work around a large CASE WHEN scenario, but before i resort to doing a CASE WHEN i'm trying my luck here.
I've tried to execute it in string format "exec('code')" where it works, but then i get another issue with getdate() that i wont go into details with.
DECLARE #ProductLines nvarchar(50) = 'usr_author'
DECLARE #searchProductlines nvarchar(50) = 'hc'
SELECT TOP 20
Productid as Produktid,
usr_Author AS Author,
Header AS Title,
usr_Publisher AS Publisher,
CustomerId AS Customerid, FROM Products
WHERE
(#ProductLines Like '%' + #searchProductlines + '%')
I've scraped away all other code that isn't relevant here. What i want to do is declare #ProductLines as the column 'usr_author' so i in the WHERE clause can use #ProductLines as an dynamic column picker in a drop down menu later.
however, this doesnt work. if i write usr_Author instead of #ProductLines, i get the results i need but then it's a static solution, rather than a dynamic solution. what is best practice in this situation?
You can only replace constant values using parameters. You cannot replace identifiers -- including column names, table names, and so on.
You can do this dynamically as:
DECLARE #col nvarchar(50) = 'usr_author'
DECLARE #search nvarchar(50) = 'hc'
DECLARE #sql NVARCHAR(MAX);
SET #sql = '
SELECT TOP 20
Productid as Produktid,
usr_Author AS Author,
Header AS Title,
usr_Publisher AS Publisher,
CustomerId AS Customerid
FROM Products
WHERE #col Like ''%'' + #search + ''%''
';
SET #sql = REPLACE(#sql, '#col', #col);
EXEC sp_executesql #sql,
N'#search nvarchar(50)',
#search=#search;
By declaring this string, you're now comparing it as a string, not a column. Try a more dynamic SQL approach:
DECLARE #ProductLines nvarchar(50) = 'usr_author'
DECLARE #searchProductlines nvarchar(50) = 'hc'
DECLARE #sql1 nvarchar(500);
select #sql1 = 'SELECT TOP 20 Productid as Produktid, usr_Author AS Author, Header AS Title, usr_Publisher AS Publisher, CustomerId AS Customerid into #temptab1 FROM Products WHERE (' + #ProductLines + 'Like ''%' + #searchProductlines + '%'')'
exec( #sql1 )
select *
from #temptab1
Disclaimer: Not injection proof in the slightest, just a concept
create procedure dbo.uspSearch(
#searchProductId int = 0,
#searchAuthor nvarchar(50) = '',
#searchHeader nvarchar(50) = '',
#searchPublisher nvarchar(50) = '',
#searchCustomerId int = 0
) as
begin
set nocount on;
select top 20
Productid as Produktid,
usr_Author AS Author,
Header AS Title,
usr_Publisher AS Publisher,
CustomerId AS Customerid
from Products
where Productid = case when #searchProductId > 0 then #searchProductId else Productid end
and usr_Author like case when #searchAuthor <> '' then '%' + #searchAuthor + '%' else usr_Author end
and Header like case when #searchHeader <> '' then '%' + #searchHeader + '%' else Header end
and usr_Publisher like case when #searchPublisher <> '' then '%' + #searchPublisher + '%' else usr_Publisher end
and CustomerId = case when #searchCustomerId > 0 then #searchCustomerId else CustomerId end;
end;
go;
declare #searchProductId int = '1'
declare #searchAuthor nvarchar(50) = 'hc'
declare #searchHeader nvarchar(50) = 'test'
declare #searchPublisher nvarchar(50) = 'test'
declare #searchCustomerId int = '1';
exec dbo.uspSearch #searchProductId, #searchAuthor, #searchHeader, #searchPublisher, #searchCustomerId;
Besides the fact, that the whole approach has a certain smell, the best answer will be: Use dynamically created SQL (if you really have / want to stick to this). There are answers already...
Just for fun, there is a fully generic approach using XML like here:
I want to use a statement like this:
SELECT *
FROM sys.objects
WHERE [name]='sysrowsets';
...With generically defined parameters
DECLARE #ColumnName VARCHAR(100)='name';
DECLARE #SearchValue VARCHAR(100)='sysrowsets';
SELECT
(
SELECT *
FROM sys.objects
FOR XML PATH('o'),TYPE
).query('/*[*[local-name()=sql:variable("#ColumnName") and text()[1]=sql:variable("#SearchValue")]]')
The result is the same row as above, but as XML (which can be transformed into a tabular set again).
<o>
<name>sysrowsets</name>
<object_id>5</object_id>
<schema_id>4</schema_id>
<parent_object_id>0</parent_object_id>
<type>S </type>
<type_desc>SYSTEM_TABLE</type_desc>
<create_date>2012-09-02T23:08:12.370</create_date>
<modify_date>2012-09-02T23:08:15.340</modify_date>
<is_ms_shipped>1</is_ms_shipped>
<is_published>0</is_published>
<is_schema_published>0</is_schema_published>
</o>
As mentioned above, this is not the recommended approach, it will be very slow. I just felt the need to say something against the impossible statements :-)
UPDATE: Tabular result and still generic...
You can use something like this, just to articulate a predicate against a unique value (in this case object_id)
SELECT o.*
FROM sys.objects o
WHERE o.object_id=
(
SELECT *
FROM sys.objects
FOR XML PATH('o'),TYPE
).query('/*[*[local-name()=sql:variable("#ColumnName")
and text()[1]=sql:variable("#SearchValue")]]')
.value('(/o/object_id/text())[1]','bigint');
I am trying to modify a stored procedure that I have to include a Input parameter #OrderBy.
But I need to test it to say if its NULL then don't include the ORDER BY in the SELECT SQL
If its NOT null then I want to order by on #OrderBy
Is this possible?
Thanks in advance
you can accomplish this using dynamic SQL
Declare #dynamicSQL nvarchar(5000);
Declare #orderBy nvarchar(50);
Set #orderBy = 'column1';
Set #dynamicSQL = 'Select * from Table ';
If (#orderBy IS NOT NULL) BEGIN
#dynamicSQL = #dynamicSQL + 'Order BY ' + #orderBy;
END
exec #dynamicSQL;
The only way to do it like you say purely is to use dynamic SQL, but a CASE statement often works just as well:
SELECT *
FROM foo f
ORDER BY CASE WHEN #OrderBy IS NULL THEN f.Whatever ELSE '' END
Hi I want to create a simple stored proecudre which does the following:
Psudocode
#tempSelect = "SELECT * FROM Table"
if (#Param is NULL) then
exec #tempSelect
else
exec #tempSelect + ' WHERE id = ' + #Param + '
Is this method efficent? Thank you.
Try
select *
from table
where id=isnull(#param, id)
Select * from Table
Where (ID = #Param or #Param is null)
Or
Select * from Table
Where ID=Coalesce(#Param, ID)
[And if you are aiming for efficiency, replace * with the specific field you want to return.]
Yes - I certainly see nothing wrong with it. You could make it even simpler though:
Set NOCOUNT On;
if (#Param is NULL)
Select * From Table;
else
Select * From Table Where (ID=#Param);
Note: I'd probably spell out the fields, though.
Depending on the case, I would probably use dynamic SQL.
However you need to remember about SQL injection in case #param originates from a user, thats why you should never add a parameter directly to your sql.
In t-sql it would look something like (out of my head and untested ;):
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = N'
SELECT ...
FROM table t
WHERE 1 = 1' (
IF(#param IS NOT NULL)
SET #SQL = #SQL + '
AND t.id = #id'
... possibly more things added to the query ...
EXEC sp_executesql
#SQL
, '#id AS INT'
, #id = #Param
By doing this, you will get an optimized query plan for each case (and by using sp_executesql, the query cache will be used as well)
I would especially avoid the OR solution, if you check the query plans generated with the OR compared to one without, you will understand why.
Try this code:
CREATE PROCEDURE [dbo].[ProcedureName]
#Param varchar(50)
AS
BEGIN
declare #tempSelect nvarchar(max)
SET NOCOUNT ON;
set #tempSelect = 'SELECT Col1, Col2 FROM Table where Col1 <> '' '
if #Param <> ''
begin
set #resultSet = #resultSet + ''' and Col1='''+#Param1
end
EXEC(#resultSet)
END