I am trying to create stored procedure, get some data from database, and then put that data into variables.
Right now i am using this:
DECLARE #CurrentRbr int;
SET #CurrentRbr =
(
SELECT Max(Rbr) + 1 As CurrentRbr
FROM Orders
WHERE OrderID=#OrderID
)
I would like to have few more variables:
DECLARE #OrderAdress varchar(50), #OrderCity varchar (20)
Can I do something like, (this of course does not work):
SET #CurrentRbr,#OrderAdress,#OrderCity =
(
SELECT Max(Rbr) + 1 As CurrentRbr,OrderAdress,OrderCity
FROM Orders
WHERE OrderID=#OrderID
)
Select is a better than Set for put that data into variables.
DECLARE #CurrentRbr int,#OrderAdress varchar(50), #OrderCity varchar (20)
SELECT #CurrentRbr = Max(Rbr) + 1 ,
#OrderAdress=OrderAdress,
#OrderCity=OrderCity
FROM Orders
WHERE OrderID=#OrderID
Group by OrderAdress, OrderCity
if you use one variable you don't need SET like below:
DECLARE #CurrentRbr int = (
SELECT Max(Rbr) + 1 As CurrentRbr
FROM Orders
WHERE OrderID=#OrderID
)
You can read another form of use variable in SQL click here
I hope you for the best
Related
Below is the TSQL I have written to fetch execute MDX using Openquery. However, I am unable to get columns in required format. Need columns title as Member only and Total column at the end of the table
DECLARE #DateFrom varchar(100)
SET #DateFrom = '2022-03-01'
DECLARE #DateTo varchar(100)
SET #DateTo = '2022-03-30'
DECLARE #Measure varchar(100)
SET #Measure = '[Measures].[Total Items]';
SET #sql = 'SELECT *
FROM OPENQUERY(POSCUBE,''
SELECT
(
[Date].[FirstDateOfWeek].Members,
{
'+#Measure+'
}
)
ON COLUMNS,
ORDER
(
(
[Product].[ProductName].Children,
[Product].[BrandName].Children
)
,
[Measures].[Total Items]
,
BDESC
)
ON ROWS
FROM [Model]
WHERE
([Date].[Date].['+#DateFrom+'] : [Date].[Date].['+#DateTo+']
) '')'
PRINT (#sql)
EXEC (#sql)
This is giving me below result
Current Result
I need result something like this where ALL (Row Total is at the end and Column Titles are Members value)
Required Result
See my code to understand what i am trying.
DECLARE #LI NVARCHAR(100)
DECLARE #XFundCode VARCHAR(20)
SET #LI = ''
MERGE INTO TblLineItemTemplate Trg
USING
(
SELECT MAX(Section) AS Section,
MAX(LineItem) AS LineItem,
MAX(XFundCode) AS XFundCode,
MAX(StandardDate) AS StandardDate,
MAX(StandardValue) AS StandardValue,
MAX(ActualProvidedByCompany) AS ActualProvidedByCompany,
MAX(TickerID) AS TickerID
FROM #TmpTenQKData
GROUP BY LineItem
) AS Src
ON UPPER(TRIM(Trg.LineItem)) = UPPER(TRIM(Src.LineItem)) AND Trg.TickerID = Src.TickerID
WHEN MATCHED THEN
UPDATE SET
XFundCode = Src.XFundCode,
Action = 'U',
Insertdate = GETDATE()
WHEN NOT MATCHED THEN
SET #LI=Src.LineItem
SET #XFundCode = Src.XFundCode
INSERT
(
TickerID,
LineItem,
XFundCode,
Action,
UserID,
Insertdate
)
VALUES
(
TRIM(#TickerID),
TRIM(#LI),
TRIM(#XFundCode),
'I', #UserID,
GETDATE()
);
i want to store this way without OUTPUT clause
SET #LI=Src.LineItem
SET #XFundCode = Src.XFundCode
is it possible ?
please tell me a way to store source table value into variable during
insert/update from merge statement.
for each insert how can i store source table value by output clause. thanks
I have a select query that returns a dataset with "n" records in one column. I would like to use this column as the parameter in a stored procedure. Below a reduced example of my case.
The query:
SELECT code FROM rawproducts
The dataset:
CODE
1
2
3
The stored procedure:
ALTER PROCEDURE [dbo].[MyInsertSP]
(#code INT)
AS
BEGIN
INSERT INTO PRODUCTS description, price, stock
SELECT description, price, stock
FROM INVENTORY I
WHERE I.icode = #code
END
I already have the actual query and stored procedure done; I just am not sure how to put them both together.
I would appreciate any assistance here! Thank you!
PS: of course the stored procedure is not as simple as above. I just choose to use a very silly example to keep things small here. :)
Here's two methods for you, one using a loop without a cursor:
DECLARE #code_list TABLE (code INT);
INSERT INTO #code_list SELECT code, ROW_NUMBER() OVER (ORDER BY code) AS row_id FROM rawproducts;
DECLARE #count INT;
SELECT #count = COUNT(*) FROM #code_list;
WHILE #count > 0
BEGIN
DECLARE #code INT;
SELECT #code = code FROM #code_list WHERE row_id = #count;
EXEC MyInsertSP #code;
DELETE FROM #code_list WHERE row_id = #count;
SELECT #count = COUNT(*) FROM #code_list;
END;
This works by putting the codes into a table variable, and assigning a number from 1..n to each row. Then we loop through them, one at a time, deleting them as they are processed, until there is nothing left in the table variable.
But here's what I would consider a better method:
CREATE TYPE dbo.code_list AS TABLE (code INT);
GO
CREATE PROCEDURE MyInsertSP (
#code_list dbo.code_list)
AS
BEGIN
INSERT INTO PRODUCTS (
[description],
price,
stock)
SELECT
i.[description],
i.price,
i.stock
FROM
INVENTORY i
INNER JOIN #code_list cl ON cl.code = i.code;
END;
GO
DECLARE #code_list dbo.code_list;
INSERT INTO #code_list SELECT code FROM rawproducts;
EXEC MyInsertSP #code_list = #code_list;
To get this to work I create a user-defined table type, then use this to pass a list of codes into the stored procedure. It means slightly rewriting your stored procedure, but the actual code to do the work is much smaller.
(how to) Run a stored procedure using select columns as input
parameters?
What you are looking for is APPLY; APPLY is how you use columns as input parameters. The only thing unclear is how/where the input column is populated. Let's start with sample data:
IF OBJECT_ID('dbo.Products', 'U') IS NOT NULL DROP TABLE dbo.Products;
IF OBJECT_ID('dbo.Inventory','U') IS NOT NULL DROP TABLE dbo.Inventory;
IF OBJECT_ID('dbo.Code','U') IS NOT NULL DROP TABLE dbo.Code;
CREATE TABLE dbo.Products
(
[description] VARCHAR(1000) NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL
);
CREATE TABLE dbo.Inventory
(
icode INT NOT NULL,
[description] VARCHAR(1000) NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL
);
CREATE TABLE dbo.Code(icode INT NOT NULL);
INSERT dbo.Inventory
VALUES (10,'',20.10,3),(11,'',40.10,3),(11,'',25.23,3),(11,'',55.23,3),(12,'',50.23,3),
(15,'',33.10,3),(15,'',19.16,5),(18,'',75.00,3),(21,'',88.00,3),(21,'',100.99,3);
CREATE CLUSTERED INDEX uq_inventory ON dbo.Inventory(icode);
The function:
CREATE FUNCTION dbo.fnInventory(#code INT)
RETURNS TABLE AS RETURN
SELECT i.[description], i.price, i.stock
FROM dbo.Inventory I
WHERE I.icode = #code;
USE:
DECLARE #code TABLE (icode INT);
INSERT #code VALUES (10),(11);
SELECT f.[description], f.price, f.stock
FROM #code AS c
CROSS APPLY dbo.fnInventory(c.icode) AS f;
Results:
description price stock
-------------- -------- -----------
20.10 3
40.10 3
Updated Proc (note my comments):
ALTER PROC dbo.MyInsertSP -- (1) Lose the input param
AS
-- (2) Code that populates the "code" table
INSERT dbo.Code VALUES (10),(11);
-- (3) Use CROSS APPLY to pass the values from dbo.code to your function
INSERT dbo.Products ([description], price, stock)
SELECT f.[description], f.price, f.stock
FROM dbo.code AS c
CROSS APPLY dbo.fnInventory(c.icode) AS f;
This ^^^ is how it's done.
A SQL database field has an array as the content (comma separated values, all integer numbers). I need to check if a number is in this array and, if yes, then the record is filtered on a select query.
A simple solution would be: suppose a function like 'Is_In' below:
select * from table where #number Is_In([fieldWithArrayContent])
I expect there is a SQL in function or even a function that can be written and used to solve this issue.
You need a splitter function - for best performane I suggest DelimitedSplit8k. Then you could just do this:
-- Sample Data
DECLARE #sometable TABLE (someid INT IDENTITY, someArray VARCHAR(1000));
INSERT #sometable(someArray)
VALUES('1,2,10,12'),('5,6,7'),('10,12,10,20,10,10'),('1,2,3'); -- id 1 & 3 have the value "10"
-- Variable
DECLARE #number INT = 10;
SELECT DISTINCT t.someid, t.someArray
FROM #sometable AS t
CROSS APPLY dbo.delimitedSplit8k(t.someArray,',') AS s
WHERE #number = s.item;
Returns:
someid someArray
----------- ------------------------
1 1,2,10,12
3 10,12,10,20,10,10
Using the same example as Alan, we can do that without a splitting function with some string manipulation as below:-
DECLARE #sometable TABLE (someid INT IDENTITY, someArray VARCHAR(1000));
INSERT #sometable(someArray)
VALUES('1,2,10,12'),('5,6,7'),('10,12,10,20,10,10'),('1,2,3'); -- id 1 & 3 have the value "10"
-- Variable
DECLARE #number INT = 1 --or 10 will work
Declare #seperator varchar(1)=','
Declare #search varchar(50)=CONCAT('%',#seperator,cast(#number as varchar(10)),#seperator,'%')
SELECT t.someid, t.someArray
FROM #sometable AS t
WHERE CONCAT(#seperator,someArray,#seperator) like #search
-- Given a CSV string like this:
declare #roles varchar(800)
select #roles = 'Pub,RegUser,ServiceAdmin'
-- Question: How to get roles into a table view like this:
select 'Pub'
union
select 'RegUser'
union
select 'ServiceAdmin'
After posting this, I started playing with some dynamic SQL. This seems to work, but seems like there might be some security risks by using dynamic SQL - thoughts on this?
declare #rolesSql varchar(800)
select #rolesSql = 'select ''' + replace(#roles, ',', ''' union select ''') + ''''
exec(#rolesSql)
If you're working with SQL Server compatibility level 130 then the STRING_SPLIT function is now the most succinct method available.
Reference link: https://msdn.microsoft.com/en-gb/library/mt684588.aspx
Usage:
SELECT * FROM string_split('Pub,RegUser,ServiceAdmin',',')
RESULT:
value
-----------
Pub
RegUser
ServiceAdmin
See my answer from here
But basically you would:
Create this function in your DB:
CREATE FUNCTION dbo.Split(#origString varchar(max), #Delimiter char(1))
returns #temptable TABLE (items varchar(max))
as
begin
declare #idx int
declare #split varchar(max)
select #idx = 1
if len(#origString )<1 or #origString is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#origString)
if #idx!=0
set #split= left(#origString,#idx - 1)
else
set #split= #origString
if(len(#split)>0)
insert into #temptable(Items) values(#split)
set #origString= right(#origString,len(#origString) - #idx)
if len(#origString) = 0 break
end
return
end
and then call the function and pass in the string you want to split.
Select * From dbo.Split(#roles, ',')
Here's a thorough discussion of your options:
Arrays and Lists in SQL Server
What i do in this case is just using some string replace to convert it to json and open the json like a table. May not be suitable for every use case but it is very simple to get running and works with strings and files. With files you just need to watch your line break character, mostly i find it to be "Char(13)+Char(10)"
declare #myCSV nvarchar(MAX)= N'"Id";"Duration";"PosX";"PosY"
"•P001";223;-30;35
"•P002";248;-28;35
"•P003";235;-26;35'
--CSV to JSON
--convert to json by replacing some stuff
declare #myJson nvarchar(MAX)= '[['+ replace(#myCSV, Char(13)+Char(10), '],[' ) +']]'
set #myJson = replace(#myJson, ';',',') -- Optional: ensure coma delimiters for json if the current delimiter differs
-- set #myJson = replace(#myJson, ',,',',null,') -- Optional: empty in between
-- set #myJson = replace(#myJson, ',]',',null]') -- Optional: empty before linebreak
SELECT
ROW_NUMBER() OVER (ORDER BY (SELECT 0))-1 AS LineNumber, *
FROM OPENJSON( #myJson )
with (
col0 varchar(255) '$[0]'
,col1 varchar(255) '$[1]'
,col2 varchar(255) '$[2]'
,col3 varchar(255) '$[3]'
,col4 varchar(255) '$[4]'
,col5 varchar(255) '$[5]'
,col6 varchar(255) '$[6]'
,col7 varchar(255) '$[7]'
,col8 varchar(255) '$[8]'
,col9 varchar(255) '$[9]'
--any name column count is possible
) csv
order by (SELECT 0) OFFSET 1 ROWS --hide header row
Using SQL Server's built in XML parsing is also an option. Of course, this glosses over all the nuances of an RFC-4180 compliant CSV.
-- Given a CSV string like this:
declare #roles varchar(800)
select #roles = 'Pub,RegUser,ServiceAdmin'
-- Here's the XML way
select split.csv.value('.', 'varchar(100)') as value
from (
select cast('<x>' + replace(#roles, ',', '</x><x>') + '</x>' as xml) as data
) as csv
cross apply data.nodes('/x') as split(csv)
If you are using SQL 2016+, using string_split is better, but this is a common way to do this prior to SQL 2016.
Using BULK INSERT you can import a csv file into your sql table -
http://blog.sqlauthority.com/2008/02/06/sql-server-import-csv-file-into-sql-server-using-bulk-insert-load-comma-delimited-file-into-sql-server/
Even the accepted answer is working fine. but I got this function much faster even for thousands of record. create below function and use.
IF EXISTS (
SELECT 1
FROM Information_schema.Routines
WHERE Specific_schema = 'dbo'
AND specific_name = 'FN_CSVToStringListTable'
AND Routine_Type = 'FUNCTION'
)
BEGIN
DROP FUNCTION [dbo].[FN_CSVToStringListTable]
END
GO
CREATE FUNCTION [dbo].[FN_CSVToStringListTable] (#InStr VARCHAR(MAX))
RETURNS #TempTab TABLE (Id NVARCHAR(max) NOT NULL)
AS
BEGIN
;-- Ensure input ends with comma
SET #InStr = REPLACE(#InStr + ',', ',,', ',')
DECLARE #SP INT
DECLARE #VALUE VARCHAR(1000)
WHILE PATINDEX('%,%', #INSTR) <> 0
BEGIN
SELECT #SP = PATINDEX('%,%', #INSTR)
SELECT #VALUE = LEFT(#INSTR, #SP - 1)
SELECT #INSTR = STUFF(#INSTR, 1, #SP, '')
INSERT INTO #TempTab (Id)
VALUES (#VALUE)
END
RETURN
END
GO
---Test like this.
declare #v as NVARCHAR(max) = N'asdf,,as34df,234df,fs,,34v,5fghwer,56gfg,';
SELECT Id FROM dbo.FN_CSVToStringListTable(#v)
I was about you use the solution mentioned in the accepted answer, but doing more research led me to use Table Value Types:
These are far more efficient and you don't need a TVF (Table valued function) just to create a table from csv. You can use it directly in your scripts or pass that to a stored procedure as a Table Value Parameter. The Type can be created as :
CREATE TYPE [UniqueIdentifiers] AS TABLE(
[Id] [varchar](20) NOT NULL
)