I have made a stored procedure. I want it to filter the data by different parameters. If I pass one parameter, it should be filtered by one; if I pass two, it should be filtered by two, and so on, but it is not working.
Can anyone help me please?
DROP PROCEDURE IF EXISTS medatabase.SP_rptProvince2;
CREATE PROCEDURE medatabase.`SP_rptProvince2`(
IN e_Region VARCHAR(45)
)
BEGIN
DECLARE strQuery VARCHAR(1024);
DECLARE stmtp VARCHAR(1024);
SET #strQuery = CONCAT('SELECT * FROM alldata where 1=1');
IF e_region IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND (regionName)'=e_Region);
END IF;
PREPARE stmtp FROM #strQuery;
EXECUTE stmtp;
END;
AFAIK, you can't have a variable argument list like that. You can do one of a couple of things:
Take a fixed maximum number of parameters, and check them for null-ness before concatenating:
CREATE PROCEDURE SP_rptProvince2(a1 VARCHAR(45), a2 VARCHAR(45), ...)
...
IF a1 IS NOT NULL THEN
SET #strQuery = CONCAT(#strQuery, ' AND ', a2);
END IF;
If you need predetermined fields to which the criteria in the argument apply (like the e_Region parameter in your existing code), then you modify the CONCAT operation appropriately.
Possible invocation:
CALL SP_rptProvince2('''North''', 'column3 = ''South''')
Take a single parameter that is much bigger than just 45 characters, and simply append it to the query (assuming it is not null).
Clearly, this places the onus on the user to provide the correct SQL code.
Possible invocation:
CALL SP_rptProvince2('RegionName = ''North'' AND column3 = ''South''')
There's not a lot to choose between the two. Either can be made to work; neither is entirely satisfactory.
You might note that there was a need to protect the strings in the arguments with extra quotes; that is the sort of thing that makes this problematic.
I found a JSON-based approach which works with the latest MySQL/MariaDB systems. Check the link below (Original Author is Federico Razzoli): https://federico-razzoli.com/variable-number-of-parameters-and-optional-parameters-in-mysql-mariadb-procedures
Basically, you take a BLOB parameter which is actually a JSON object and then do JSON_UNQUOTE(JSON_EXTRACT(json object, key)) as appropriate.
Lifted an extract here:
CREATE FUNCTION table_exists(params BLOB)
RETURNS BOOL
NOT DETERMINISTIC
READS SQL DATA
COMMENT '
Return whether a table exists.
Parameters must be passed in a JSON document:
* schema (optional). : Schema that could contain the table.
By default, the schema containing this procedure.
* table : Name of the table to check.
'
BEGIN
DECLARE v_table VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.table'));
DECLARE v_schema VARCHAR(64)
DEFAULT JSON_UNQUOTE(JSON_EXTRACT(params, '$.schema'));
IF v_schema IS NULL THEN
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = SCHEMA()
AND TABLE_NAME = v_table
);
ELSE
RETURN EXISTS (
SELECT TABLE_NAME
FROM information_schema.TABLES
WHERE
TABLE_SCHEMA = v_schema
AND TABLE_NAME = v_table
);
END IF;
END;
Create Proc getinfo
(
#Studentid int,
#karan varchar(200) out
)
As
Begin
select #karan= Firstname+' '+Lastname from tbl_students where Studentid=#studentid
End
enter image description here
There is nothing wrong with the getinfo proc. Your exec statement is not quite right and need to declare and assign a variable to accept the returned value, and you need a select to see it
declare #name varchar(100)
set #name = ' '
execute getinfo #studentid = 11000,#karan = #name output
select #name name
note this is a sql server answer since that appears to be what you are using.
I have written the code as follows:
CREATE PROCEDURE [dbo].[TEST22]
#database_name VARCHAR(200),
/*Enter the Start Date and End date in the format MM/DD/YYYY*/
#Start_Date VARCHAR(200),
#End_Date VARCHAR(200)
AS
BEGIN
DECLARE #querystring VARCHAR(MAX)
--JOINS THE VENDOR MASTER FILE WITH THE PAYMENTS FILE TO IDENTIFY VENDORS WHO DO
-- NOT APPEAR IN THE PAYMENTS FILE FOR THE GIVEN PERIOD OF ANALYSIS
SET NOCOUNT ON;
IF EXISTS(SELECT * FROM SYSOBJECTS WHERE ID = OBJECT_ID('dummy1'))
DROP TABLE dummy1
DECLARE #Test Nvarchar(50)
SET #test =N'"Test1"'
SET #querystring = 'select #test as Test1, getdate() as time1
into dbo.dummy1
from sys.tables'
EXEC (#querystring)
END
and when I run this code I am getting the following error:
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "#test".
Please suggest what the error is?
SET #querystring ='select #test as Test1, getdate() as time1
When this query is executing, it is executing as a standalone query, in which #test is not defined
EXEC (#querystring)
during this execution, the parameter #test must be defined.
In my opinion what you wish to achieve is
SET #querystring = 'select ' + #test + ' as Test1, getdate() as time1 ..... '
This concatenation will put the value of #test in the query and the new query will be
SELECT N'"Test1"' as Test1, getdate() as time1....
Glad to help! Please remember to accept the answer if you found it helpful.
As #Manish points out, #test isn't defined within the scope of your EXEC statement. However, a cleaner way to work with it than their proposal is to use sp_executesql:
SET #querystring = 'select #test as Test1, getdate() as time1
into dbo.dummy1
from sys.tables'
DECLARE #Parms nvarchar(max)
SET #Parms = N'#test nvarchar(50)'
EXEC sp_executesql #querystring,#Parms,#test
This keeps it as a separate parameter within the inner scope of the EXEC and so avoids any potential concerns about SQL Injection that arise whenever you concatenate SQL code and data.
I have a table called Std_Components which acts like an index for list of components with associated tables. The column AssociatedTable holds the name of table that actually contains the component data.
Please check images below -
Here is table data for Std_SteeringPumps
I am trying to create a stored procedure that will copy Std_Components table as well as all associated tables with new name. For ex. Lets say if i provided 001 as a parameter to this stored procedure i should be able create new tables like C001_Components, C001_SteeringPumps and so on.
This is what I have done so far:
ALTER PROCEDURE [dbo].[sgi_sp_CreateTablesForNewCompany]
-- Add the parameters for the stored procedure here
#CompanyId varchar(5)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- declare variables
declare #qry as varchar(2000)
declare #compTblName as varchar(100)
set #compTblName = 'C'+#companyId +'_Components'
-- Check if table already exists
IF object_id(#compTblName) is not null
return
-- Create main component index table by copying standard component table --
set #qry = 'Select * into '+#compTblName+' From Std_Components;';
--print #qry
--execute (#qry)
set #qry =#qry + 'Update C'+#companyId +'_Components Set AssociatedTable=''C'+#companyId +'''+substring(AssociatedTable,4,200);';
--print #qry
--exec #qry
-- Create all child tables --
Select * Into #TempTbl From dbo.Std_Components
Declare #Id int
While (Select Count(*) From #TempTbl) > 0
Begin
declare #rowTableName as varchar(50)
declare #compNewTbl as varchar(50)
Select Top 1 #rowTableName=AssociatedTable, #Id = Id From #TempTbl
set #compNewTbl = 'C'+#companyId + substring(#rowTableName,4,200);
set #qry = #qry + 'Select * into '+#compNewTbl+' From ' + #rowTableName + ';'
--print #qry
--exec #qry
Delete #TempTbl Where Id = #Id
End
print #qry
exec #qry
END
Here is the output of the print statement for the query it generates -
Select * into C001_Components From Std_Components;
Update C001_Components Set AssociatedTable='C001'+substring(AssociatedTable,4,200);
Select * into C001_SteeringPumps From Std_SteeringPumps;
But when the stored procedure is executed, I get the following error -
Msg 203, Level 16, State 2, Procedure sgi_sp_CreateTablesForNewCompany, Line 56
The name 'Select * into C001_Components From Std_Components;Update C001_Components Set AssociatedTable='C001'+substring(AssociatedTable,4,200);Select * into C001_SteeringPumps From Std_SteeringPumps;' is not a valid identifier.
Can anybody help me out resolve this issue.
Thanks for sharing your time and wisdom.
The error you're getting is because the EXEC statement (the last line of the stored procedure) needs to have brackets around the #qry variable so that it becomes
exec(#qry)
Without the brackets it's treating the entire SQL string as stored procedure name.
The non valid indentifier is around the AssociatedTable part
Set AssociatedTable='C001'+substring(AssociatedTable,4,200); will not run as there is no scope for AssociatedTable to substring - the string needs to contain the name of the table completely to be able to be executed
Instead of
exec #qry;
You need
exec sp_executesql #qry;
You'll also need to change the type of #qry to NVARCHAR. Note that because of the dynamic sql, the proc is prone to SQL Injection and other escaping issues (i.e. ensure that #CompanyId is validated)
I have a table with the following data in one column:
abc,2,2,34,5,3,2,34,32,2,3,2,2
def,2,2,34,5,3,2,34,32,2,3,2,2
I want to take this data and insert it into another table, using the commas as delimiters, just like how you can specify the FIELDTERMINATOR in BULK INSERT statements.
Is there a way to do this using T-SQL?
I'm not sure if there is any direct way to do in the T-SQL , but if you want to use Bulk Insert you can use sqlcmd to export to CSV file and then Import the file back into server using Bulk Insert.
Create a dbo.Split Functionm, you can refer here split string into multiple record
There are tons of good examples.
if you want to execute as batch process, You can execute sqlcmd and 'Bulk Insert'
sqlcmd -S MyServer -d myDB -E -Q "select dbo.Split(col1) from SomeTable"
-o "MyData.csv" -h-1 -s"," -w 700
-s"," sets the column seperator to
bulk insert destTable
from "MyData.csv"
with
(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)
Otherwise, You can manipulate directly in the T-SQL, but given you have the same identify of columns definition.
INSERT INTO DestinationTable
SELECT dbo.Split(col1) FROM SomeTable
You need to use a Split function to split your string into a table variable, and then insert those values into your table.
There are tons of those split functions out there, with various pros and cons and various number of parameters and so forth.
Here is one that I quite like - very nicely done, clearly explained.
With that function, you should have no trouble converting your column into individual entries for your other table.
EDIT: allow multiple char separators
This is how I solved it, with two functions to do the splitting into columns (if you want a more complete solution with line splitting as well, see my other post here). It involves:
A scalar function (fSubstrNth) for extracting the n-th field of a line, given an separator
A scalar function (fPatIndexMulti) for finding the n-th index of the separator
(Optional) alternative Right function to accept negative values
Finally, some specific code to use in your solution, since SQL doesn't allow dynamic table-function definitions (in other words, you can't SELECT from a function with dynamic columns)
Now, for the code snippets:
fSubstrNth
-- =============================================
-- Author: Bernardo A. Dal Corno
-- Create date: 18/07/2017
-- Description: substring com 2 PatIndex limitando inicio e fim
-- =============================================
CREATE FUNCTION fSubstrNth
(
#Text varchar(max),
#Sep varchar(3),
#N int --Nth campo
)
RETURNS varchar(max)
AS
BEGIN
DECLARE #Result varchar(max)
IF #N<1 RETURN ''
IF #N=1
SET #Result = substring(#Text, 1, dbo.fPatIndexMulti(#Sep,#Text,1)-1)
ELSE
SET #Result = substring(#Text, dbo.fPatIndexMulti(#Sep,#Text,#N-1)+LEN(#Sep), CASE WHEN dbo.fPatIndexMulti(#Sep,#Text,#N)>0 THEN dbo.fPatIndexMulti(#Sep,#Text,#N)-dbo.fPatIndexMulti(#Sep,#Text,#N-1)-LEN(#Sep) ELSE LEN(#Text)+1 END)
RETURN #Result
END
fPatIndexMulti
-- =============================================
-- Author: Bernardo A. Dal Corno
-- Create date: 17/07/2017
-- Description: recursive patIndex
-- =============================================
CREATE FUNCTION [dbo].[fPatIndexMulti]
(
#Find varchar(max),
#In varchar(max),
#N tinyint
)
RETURNS int
AS
BEGIN
DECLARE #lenFind int, #Result int, #Texto varchar(max), #index int
DECLARE #i tinyint=1
SET #lenFind = LEN(#Find)-1
SET #Result = 0
SET #Texto = #In
WHILE (#i <= #N) BEGIN
SET #index = patindex('%'+#Find+'%',#Texto)
IF #index = 0 RETURN 0
SET #Result = #Result + #index
SET #Texto = dbo.xRight(#Texto, (#index + #lenFind)*-1)
SET #i = #i + 1
END
SET #Result = #Result + #lenFind*(#i-2)
RETURN #Result
END
xRight
-- =============================================
-- Author: Bernardo A. Dal Corno
-- Create date: 06/01/2015
-- Description: Right inverso (para nros < 0)
-- =============================================
CREATE FUNCTION [dbo].[xRight]
(
#Texto varchar(8000),
#Qntd int
)
RETURNS varchar(8000)
AS
BEGIN
DECLARE #Result varchar(8000)
IF (Len(#Texto) = 0) OR (#Qntd = 0)
SET #Result = ''
ELSE IF (#Qntd > 0)
SET #Result = Right(#Texto, #Qntd)
ELSE IF (#Qntd < 0)
SET #Result = Right(#Texto, Len(#Texto) + #Qntd)
RETURN #Result
END
Specific code
SELECT
acolumn = 'any value',
field1 = dbo.fSubstrNth(table.datacolumn,',',1),
field2 = dbo.fSubstrNth(table.datacolumn,',',2),
anothercolumn = 'set your query as you would normally do',
field3 = (CASE dbo.fSubstrNth(table.datacolumn,',',3) WHEN 'C' THEN 1 ELSE 0 END)
FROM table
Note that:
fSubstrNth receives the n-th field to extract from the 'datacolumn'
The query can be as any other. This means it can be stored in a procedure, tabled-function, view, etc. You can extract some or all fields, in any order you wish, and process however you want
If used in a stored procedure, you could create a generic way of creating a query and temp table that loads the string with dynamic columns, but you have to make a call to another procedure to use the data OR create a specific query like above in the same procedure (which would make it non-generic, just more reusable)