I'm trying to replicate a crystal report with a dynamic parameter. If I type a string into the parameter screen and click the arrow button, it adds the parameter to a list:
The resulting query looks like this, but the list can grow with additional OR #param clauses:
SELECT * FROM table_name WHERE #param LIKE 'cfe%' OR #param LIKE 'abr%'
How can I create a list like that in SSRS to contain several parameters? The query below is one possible example:
SELECT * FROM table_name WHERE
#param LIKE 'cfe%'
OR #param LIKE 'abr%'
OR #param LIKE 'fez%'
OR #param LIKE 'zez%'
I tried using multiple values in the parameter, but as soon as I do I can't type in the parameter box:
I tried this but it did not work. I also tried to use CONTAINS but there are no indexes for a view.
You can use a single parameter string. The user separates entries in the list with a comma. The query got a lot worse though but I have you an example of variable number of prefixes with comma separated.
I hope this helps. Its not as simple or as pretty.
In the dataset, use a similar query or proc that uses the parameter like so and it should work:
Declare #prefixes varchar(1000)
set #prefixes='abc,defg,efgh,hij,jkl,mno'
declare #sql nvarchar(max) = ''
declare #currentint int
set #currentint = 1
declare #maxint int
set #maxint = len(#prefixes) - len(replace(#prefixes, ',', '')) + 1
declare #currentcommaposition int
set #sql = 'IF OBJECT_ID(''tempdb..#tempTest'') IS NOT NULL DROP TABLE #tempTest
create table #tempTest
(
ID INT,
name varchar(100)
)
insert into #tempTest
(id,name)
select 1,''abcd''
union
select 2, ''defghijk''
union
select 3,''efghoot''
union
select 4,''hijack''
union
select 5,''jklmo''
union
select 6,''mnopoly''
union
select 7,''pqrstuv''
union
select 8,''tubool''
IF OBJECT_ID(''tempdb..#testresults'') IS NOT NULL DROP TABLE #testresults
create table #testresults
(
id int, name varchar(100)
)
declare #prefixes varchar(100) = ''' + #prefixes + ',''' + char(10) + ' declare #currentint int declare #maxint int = ' + convert(varchar(10),#maxint) + char(10)
while ( #currentint <= #maxint )
begin
set #sql = #sql + 'set #currentint = ' + convert(varchar(10),#currentint) + ' declare #suffix' + convert(varchar(2), #currentint) + ' VARCHAR(100)' + char(10)
+ 'set #suffix' + convert(varchar(2), #currentint) + '= substring(#prefixes,0,charindex('','',#prefixes))' + char(10)
+
'set #prefixes=Right(#prefixes,len(#prefixes)-charindex('','',#prefixes))' + char(10) +
'insert into #testresults (id, name)
select id, name from #temptest t where t.name like #suffix' + convert(varchar(2), #currentint) + ' + ''%''' + char(10)
+ 'if (#currentint = #maxint) begin select * from #testresults end ' + char(10)
set #currentint = #currentint + 1
end
exec sp_executesql #sql
The second option for you will be having parameters for each suffix a user can answer and allow them to be blank or null as the default. This will limit the number of prefixes the user can enter, but I do think you should be able to guesstimate the max number a user would enter. Or the user can run the report multiple times and when they do they export to excel to mash the reports together if they want to.
This is a bit easier to understand for the developer but more work for the user.
So in your stored procedure you will then use a statement like below:
select *
from dbo.Test t
WHERE
( ISNULL(#Prefix1,'') <> '' AND t.TestName LIKE #Prefix1 + '%')
OR
( ISNULL(#Prefix2,'') <> '' AND t.TestName LIKE #Prefix2 + '%')
OR
( ISNULL(#Prefix3,'') <> '' AND t.TestName LIKE #Prefix3 + '%')
OR
( ISNULL(#Prefix4,'') <> '' AND t.TestName LIKE #Prefix4 + '%')
OR
( ISNULL(#Prefix5,'') <> '' AND t.TestName LIKE #Prefix5 + '%')
OR
( ISNULL(#Prefix6,'') <> '' AND t.TestName LIKE #Prefix6 + '%')
OR
( ISNULL(#Prefix7,'') <> '' AND t.TestName LIKE #Prefix7 + '%')
OR
( ISNULL(#Prefix8,'') <> '' AND t.TestName LIKE #Prefix8 + '%')
OR
( ISNULL(#Prefix9,'') <> '' AND t.TestName LIKE #Prefix9 + '%')
OR
( ISNULL(#Prefix10,'') <> '' AND t.TestName LIKE #Prefix10 + '%')
Related
I am developing a custom application using CodeIgniter and MSSQL Server. Here i am using stored procedures.
Now i am wondering to implement codeigniter query type functionality where i can create a universal stored procedure in SQL Server and at the time of using i can pass tablename, array of fields and values.
It can work for both insert and update.
Something like we do in CodeIgniter to execute the query,
$data = array('fieldname1' => 'value1',
'fieldname2' => 'value2');
$this->db->insert($tablename,$data);
Just like this if we can pass the table name and array of the data to stored procedure and stored procedure automatically execute it.
If this can be done, it can save lots n lots of man hours. If anyone have already done i will be very much happy to see the solution.
You need to make string very specific in this case.
Figure out your table name, Column name, Column values for insert. For update 2 more parameters are required Id column name and its value.
GO
---- exec InsertUpdate 'tablename', 'col1, col2, col3', 'val1, val2, val3', 'idcol', 'idval'
GO
Create proc InsertUpdate
( #TableName nvarchar(500),
#ColName nvarchar(max),
#ColValues nvarchar(max),
#IDColName nvarchar(100) = '', --- for update only otherwise null
#IdColValue nvarchar(Max) = '' --- for update only otherwise null
)
As
Begin
declare #Query nvarchar(max)
if (#IdColValue = '')
Begin
set #Query = ' Insert into ' + #TableName + ' (' + #ColName + ') values (' + #ColValues + ')'
End
Else
Begin
;with CtColumn as (
select ROW_NUMBER() over (order by (select 1000)) as Slno, * from Split(#ColName,',') )
, CtValue as (
select ROW_NUMBER() over (order by (select 1000)) as Slno, * from Split(#ColValues, ','))
, CTFinal as (
select CCOl.Slno, CCOl.Items as ColName, CVal.Items as ColValue from CtColumn as CCOl inner join CtValue as CVal on CCOl.Slno=CVal.Slno )
select #Query = 'update ' + #TableName + ' set ' +
stuff ( (select ',' + ColName + '=' + ColValue from CTFinal for xml path ('')) ,1,1,'') +
' where ' + #IDColName + '=' + #IdColValue
End
exec sp_executesql #Query
End
Go
I'm building a layer over an application that needs to catch the changes happens to the data and update another system with these changes (SIF) and I faced a problem with a specific table, the application truncates the table, and insert a new set of records every time the data reconciled.
In order to solve this problem, I used a shadow table and Merged the records from the original table, and as I found that I might use the same method with other tables in the future, I created a generic SP that reads the structure of the tow tables and constructs a merge statement then runs it and I'm sharing the SP in the first answer, hope someone makes use of it, any comment or question is welcomed.
The SP works as long as the two tables are identical and the change tracking is working beautifully.
1- Creating the SP
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE Procedure [Compare2Tables](
#DestSchema as NVarchar(255) ,
#DestTable as NVarchar(255),
#SrcSchema as NVARCHAR(255) ,
#srcTable as NVARCHAR(255) ,
#AdditionalCondition as NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #JoiningFields as NVARCHAR(MAX)
DECLARE #MismatchingCondition as NVARCHAR(MAX)
DECLARE #UpdateOtherFields as NVARCHAR(MAX)
DECLARE #InsertDestFields as NVARCHAR(MAX)
DECLARE #InsertSrcFilds as NVARCHAR(MAX)
DECLARE #TheSQL as NVARCHAR(MAX)
DECLARE #CurrentColumn as NVARCHAR(255)
DECLARE #CurrentConstraint as NVARCHAR(255)
DECLARE #tablespecs TABLE (
TABLE_SCHEMA nvarchar(255) ,
TABLE_NAME nvarchar(255) ,
COLUMN_NAME nvarchar(255) ,
CONSTRAINT_NAME nvarchar(255)
)
insert into #tablespecs SELECT DISTINCT T.TABLE_SCHEMA , T.TABLE_NAME , T.COLUMN_NAME ,CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.COLUMNS t
LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE K ON T.TABLE_NAME = K.TABLE_NAME AND T.TABLE_SCHEMA = K.TABLE_SCHEMA AND T.COLUMN_NAME = K.COLUMN_NAME
WHERE T.TABLE_NAME = #DestTable
AND T.TABLE_SCHEMA = #DestSchema
set #JoiningFields = ' '
set #MismatchingCondition = ' '
set #UpdateOtherFields = ' '
set #InsertDestFields = ' '
set #InsertSrcFilds = ' '
while exists (select * from #tablespecs)
Begin
set #CurrentColumn = (Select top 1 Column_name from #tablespecs)
--select #CurrentColumn
Set #CurrentConstraint = (Select CONSTRAINT_NAME FROM #tablespecs WHERE COLUMN_NAME = #CurrentColumn)
if not #CurrentConstraint is null
set #JoiningFields = #JoiningFields + ' D.' + #CurrentColumn + '=S.' + #CurrentColumn + ' AND '
ELSE
begin
SET #MismatchingCondition = #MismatchingCondition + ' ISNULL(D.' + #CurrentColumn + ',0) <> ISNULL(S.' + #CurrentColumn + ',0) OR '
SET #updateOtherFields = #updateOtherFields + 'D.' +#CurrentColumn + ' = S.' + #CurrentColumn + ','
end
set #InsertDestFields = #InsertDestFields + #CurrentColumn + ','
set #InsertSrcFilds = #InsertSrcFilds + 'S.' + #CurrentColumn + ',';
delete from #tablespecs where Column_Name = #CurrentColumn
End
SET #JoiningFields = SUBSTRING(#JoiningFields , 1 , len(#JoiningFields) - 4)
SET #MismatchingCondition = SUBSTRING(#MismatchingCondition , 1 , len(#MismatchingCondition) - 3)
SET #UpdateOtherFields = SUBSTRING(#UpdateOtherFields , 1 , len(#updateOtherFields) - 1)
SET #InsertDestFields = SUBSTRING(#InsertDestFields , 1 , len(#InsertDestFields) - 1)
SET #InsertSrcFilds = SUBSTRING(#InsertSrcFilds , 1 , len(#InsertSrcFilds) - 1)
--select #JoiningFields JoiningFields , #UpdateOtherFields UpdateOtherFields , #MismatchingCondition MismatchingCondition , #InsertDestFields InsertDestFields , #InsertSrcFilds InsertSrcFilds
set #TheSQL = 'MERGE INTO ' + #DestSchema + '.' + #DestTable + ' AS D using (SELECT * FROM ' + #SrcSchema+'.'+ #SrcTable + ' ' + #AdditionalCondition + ') AS S ON ' + #JoiningFields + ' WHEN MATCHED AND (' + #MismatchingCondition + ')
THEN UPDATE SET ' + #updateOtherFields + '
WHEN NOT MATCHED BY TARGET THEN
INSERT (' + #InsertDestFields + ')
VALUES (' + #InsertSrcFilds + ')
WHEN NOT MATCHED BY SOURCE THEN
DELETE;'
EXECUTE sp_executesql #TheSQL
END
2- Now see the implementation
--Create theSource table
CREATE TABLE TheSource
(
TheID INT PRIMARY KEY,
TheName VARCHAR(100),
TheCost MONEY,
ProductionYear VARCHAR(4)
)
GO
--Fill some records in TheSource
INSERT INTO TheSource
VALUES
(1, 'Word', 10.00,'2018'),
(2, 'Access', 20.00,'2018'),
(3, 'Excel', 30.00,'2017'),
(4, 'PowerPoint', 40.00,'2017')
GO
--Create Destination table
CREATE TABLE TheDest
(
TheID INT PRIMARY KEY,
TheName VARCHAR(100),
TheCost MONEY,
ProductionYear VARCHAR(4)
)
GO
--The Dest table is left with no records on purpose
SELECT * FROM TheSource
SELECT * FROM TheDest
GO
--The folloing syntax will fill only products of 2017
execute [Compare2Tables] 'dbo','TheDest','dbo', 'TheSource','Where ProductionYear = 2017'
SELECT * FROM TheDest
-- Syncronizing all records regardless of the year
execute [Compare2Tables] 'dbo','TheDest','dbo', 'TheSource',' '
SELECT * FROM TheDest
--Updating one row in the source, then sync
update TheSource set TheCost = 33.00 where TheName = 'Access'
execute [Compare2Tables] 'dbo','TheDest','dbo', 'TheSource',' '
SELECT * FROM TheDest
-- updating all records in the source, then sync
update TheSource set TheCost = TheCost * 0.75
execute [Compare2Tables] 'dbo','TheDest','dbo', 'TheSource',' '
SELECT * FROM TheDest
I'm trying to achieve a advanced search functionality for my application in which i have a SQL Table Valued Parameter in the following structure,
ColumnName Operator Keyword
------------------------------------
Name StartsWith Ram
City Equals Chennai
My SQL table,
Name City CreatedDate
-----------------------------------
Ram Chennai 10/10/2014
Ramachan Kovai 02/03/2015
How can i loop thorough this TVP so that i can build the WHERE clause and can append it to the SELECT query which is faster since i have some 10 rows of search values(criteria).
The filters are associated with AND operator.
List of operators used:
Equals
Not equals
Starts with
Ends with
From(Date)
To(Date)
You can create a dynamic filtered expression like below and use it in your SQL. You need to be very careful when adding editing filters in your TVP and verifying it against respective datatypes as well
Create Type and Base Table with Data
/*
CREATE TYPE FilterTVP AS TABLE
(
ColumnName VARCHAR(30), Operator VARCHAR(30), Keyword VARCHAR(30)
);
GO
CREATE TABLE myTable
(
Name VARCHAR(50),
City VARCHAR(50),
CreatedDate DATE
)
INSERT INTO myTable VALUES('Ram','Chennai','10/10/2014'),('Ramachan','Kovai','02/03/2015')
*/
Query
DECLARE #Param FilterTVP
INSERT INTO #Param VALUES('Name','StartsWith','Ram'),('City','Equals','Chennai'),('CreatedDate','From','2014-05-05')
DECLARE #FilterExp NVARCHAR(MAX)
SELECT #FilterExp =
(SELECT
' AND ' + QUOTENAME(ColumnName,'[') + ' ' +
CASE Operator
WHEN 'Equals'
THEN '='
WHEN 'Not equals'
THEN '<>'
WHEN 'StartsWith'
THEN 'LIKE'
WHEN 'Endswith'
THEN 'LIKE'
WHEN 'From'
THEN '>='
WHEN 'To'
THEN '<='
END + ' ' +
CASE
WHEN Operator = 'Startswith' THEN QUOTENAME(Keyword + '%','''')
WHEN Operator = 'Endswith' THEN QUOTENAME('%' + Keyword ,'''')
ELSE QUOTENAME(Keyword,'''')
END
FROM #Param
FOR XML PATH(''),TYPE).value('.','NVARCHAR(MAX)')
SET #FilterExp = 'SELECT * FROM myTable WHERE 1=1 ' + ISNULL(#FilterExp,'')
PRINT #FilterExp
EXEC sp_executeSQL #FilterExp
Output
SQL Fiddle
Name City CreatedDate
--------------------------
Ram Chennai 2014-10-10
Build your statement and then execute it like for example:
CREATE TABLE f
(
ColumnName NVARCHAR(MAX) ,
Operator NVARCHAR(MAX) ,
KeyWord NVARCHAR(MAX)
)
CREATE TABLE t
(
Name NVARCHAR(MAX) ,
City NVARCHAR(MAX)
)
INSERT INTO f
VALUES ( 'Name', 'StartsWith', 'Ram' ),
( 'City', 'Equals', 'Chennai' )
INSERT INTO t
VALUES ( 'Ram', 'Chennai' ),
( 'Ramachan', 'Kovai' )
DECLARE #op NVARCHAR(MAX) ,
#v NVARCHAR(MAX)
DECLARE #statement NVARCHAR(MAX) = 'SELECT * FROM t WHERE Name '
SELECT #op = Operator ,
#v = KeyWord
FROM f
WHERE ColumnName = 'Name'
SET #statement = #statement + CASE #op
WHEN 'StartsWith' THEN 'LIKE ''' + #v + '%'''
ELSE ' = ''' + #v + ''''
END + ' AND City'
SELECT #op = Operator ,
#v = KeyWord
FROM f
WHERE ColumnName = 'City'
SET #statement = #statement + CASE #op
WHEN 'StartsWith' THEN 'LIKE ''%' + #v + '%'''
ELSE ' = ''' + #v + ''''
END
EXEC(#statement)
Output:
Name City
Ram Chennai
I have the following SQL query in stored procedure and trying to call Table-value function(fn_get_type_ids). I am getting 'Must declare the scalar variable "#category_id".' error message. Table-value function returns muliple IDs. How do I call this function?
The stored procedure works if I pass hard coded values to the function.
dbo.fn_get_type_ids(2, 90, NULL). It doesn't work if I pass variables shown below. What could be wrong? please suggest.
ALTER PROCEDURE [dbo].[get_search_results]
#user_id BIGINT,
#category_id INT = NULL,
#a_id INT = NULL,
#all_detail_id INT = NULL
AS
BEGIN
DECLARE #select_list VARCHAR(4000)
DECLARE #where_condition VARCHAR(4000)
SELECT #select_list = 'SELECT DISTINCT c.table1_id, c.table1_number, c.type_id '
SELECT #select_list = #select_list + ' FROM dbo.TABLE1 c '
SELECT #select_list = #select_list + ' LEFT JOIN TABLE2 cb ON cb.table1_id = c.table2_id '
SELECT #where_condition = ' WHERE c.active_flag = 1'
SELECT #where_condition = #where_condition + ' AND c.type_id in
(select type_id from dbo.fn_get_type_ids(#category_id, #a_id, #all_detail_id)) '
END
You are using dynamic sql and sql variables exist in different sessions, to make it work you need to change it to:
SELECT #where_condition = #where_condition + ' AND c.type_id in
(select type_id from dbo.fn_get_type_ids('+CAST(#category_id as varchar))+',
'+CAST(#a_id as varchar))+', '+CAST(#all_detail_id as varchar))+')) '
Try making that last select:
SELECT #where_condition = #where_condition + ' AND c.type_id in
(select type_id from dbo.fn_get_type_ids(' + ISNULL(#category_id,'NULL') + ', ' + ISNULL(#a_id,'NULL') + ', ' + ISNULL(#all_detail_id,'NULL') + ')) '
This solution is for an unbounded Gridview paging and having problem with the syntax of this query:
> #currTable varchar(20),
#startRowIndex int,
#maximumRows int,
#totalRows int OUTPUT
AS
DECLARE #first_id int, #startRow int
IF #startRowIndex = 1
SET #startRowIndex = 1
ELSE
SET #startRowIndex = ((#startRowIndex - 1) * #maximumRows)+1
SET ROWCOUNT #startRowIndex
DECLARE #sql varchar(250);
SET #sql = 'SELECT ID, StringID_from_Master, GUID, short_Text, lang_String, date_Changed, prev_LangString, needsTranslation, displayRecord, brief_Descrip FROM ' + #currTable + ' ';
EXECUTE(#sql);
PRINT #first_id
SET ROWCOUNT #maximumRows
SELECT #sql = 'SELECT ' + CAST(#first_id as varchar(20)) + ' = ID FROM ' + QUOTENAME(#currTable) + ' ORDER BY ID ' ;
EXEC (#sql);
SET ROWCOUNT 0
-- Get the total rows
SET #sql = 'SELECT ' + + CAST(#totalRowsas varchar(20)) + ' = COUNT(ID) FROM ' + #currTable + ' ';
EXECUTE(#sql);
RETURN
<
The errors is:
Conversion failed when converting the varchar value ''SELECT ' to data type int.
Tried also
nvarchar and varchar. = + CAST(#first_id as varchar(10)) +
If you're trying to implement paging, this is wrong in so many ways. First, you're using SET ROWCOUNT to limit to #startRowIndex, but then you're selecting ALL n rows (with no ORDER BY), then getting the first ID, then counting the total rows by selecting from the table? Might I suggest a better approach?
CREATE PROCEDURE dbo.PageSmarter
#Table NVARCHAR(128), -- table names should not be varchar(20)
#FirstRow INT,
#PageSize INT,
#TotalRows INT OUTPUT
AS
BEGIN
SET NOCOUNT ON; -- always, in every stored procedure
DECLARE
#first_id INT,
#startRow INT,
#sql NVARCHAR(MAX);
SET #sql = N'WITH x AS
(
SELECT
ID,
rn = ROW_NUMBER() OVER (ORDER BY ID)
FROM
' + #Table + '
)
SELECT rn, ID
INTO #x FROM x
WHERE rn BETWEEN ' + CONVERT(VARCHAR(12), #FirstRow)
+ 'AND (' + CONVERT(VARCHAR(12), #FirstRow)
+ ' + ' + CONVERT(VARCHAR(12), #PageSize) + ' - 1);
SELECT first_id = MIN(ID) FROM #x;
SELECT
ID, StringID_from_Master, GUID, short_Text, lang_String, date_Changed,
prev_LangString, needsTranslation, displayRecord, brief_Descrip
FROM ' + #Table + ' AS src
WHERE EXISTS
(
SELECT 1 FROM #x
WHERE ID = src.ID
);';
EXEC sp_executeSQL #sql;
SELECT #totalRows = SUM(row_count)
FROM sys.dm_db_partition_stats
WHERE [object_id] = OBJECT_ID(#Table);
END
GO
DECLARE #tr INT;
EXEC dbo.PageSmarter 'dbo.tablename', 10, 2, #tr OUTPUT;
SELECT #tr;
I haven't tested all edge cases with this specific implementation. I will confess, there are much better ways to do this, but they usually aren't complicated with the additional requirement of dynamic table names. This suggests that there is something inherently wrong with your design if you can run the exact same queries against any number of tables and get similar results.
In any case, you can review some of the (quite lengthy) discussion about various approaches to paging over at SQL Server Central:
http://www.sqlservercentral.com/articles/T-SQL/66030/
There are 62 comments following up on the article:
http://www.sqlservercentral.com/Forums/Topic672980-329-1.aspx
I am guessing your #first_id field is an int. If so, then you need to CAST/Convert your #first_id value to a string/varchar.
CAST(#first_id as varchar(10))
or
Convert(varchar(10), #first_id)
MSDN documentation on CAST/Convert for SQL server
EDIT: After looking at your query again, I notice that you are setting your #first_id = ID, This is incorrect syntax, the correct syntax would be below.
SELECT #sql = 'SELECT ID AS ' + CAST(#first_id as varchar(10)) + ' FROM ' +
QUOTENAME(#currTable) + ' ORDER BY ID ' ;
EXEC (#sql);
It appears you're trying to create an alias for your column ID. The string you're building won't result in a valid SQL statement if it contains a number. It would come out to something like this:
SELECT 123 = ID FROM dbo.MyTable ORDER BY ID
Try this:
SELECT ID AS '123' FROM dbo.MyTable ORDER BY ID
To achieve that:
SELECT #sql = 'SELECT ID AS ''' + CAST(#first_id as varchar(10)) +
''' FROM ' + QUOTENAME(#currTable) +
' ORDER BY ID ' ;
I would do it this way
create table #e (a int)
SET #sql = 'insert #e SELECT COUNT(ID) FROM ' + #currTable + ' ';
exec(#sql)
select #totalRows = a from #e
drop table #e