How to pass multiple parameter in where condition in SQL - sql-server

I have a query to get the order details like below:
declare #orderNo nvarchar(10) = '12345'
begin
select * from orderProperty where CorderID =(select id from Corder where orderno = #orderNo)
End
Its working fine for single order.
However, if I have multiple order numbers ( lets say, 3000) ; How can I get all data in a single query - by passing multiple parameters( that is multiple order numbers) into it?
Thanks.

Create a user-defined table type, then declare a parameter as that table type. Then you can pass your orders into that table parameter and use it as you would any other regular table.
-- create user-defined table type
CREATE TYPE [dbo].[ordersUdt] AS TABLE(
[orderNo] nvarchar(10) NULL
)
GO
-- declare table variable as new type
-- insert order records into table variable
declare #records ordersUdt
insert into #records ([orderNo])
values ('12345'),('34567'),('56789')
-- use table variable as you would regular table
select *
from orderProperty op
join Corder c
on op.CorderID = C.id
join #records r
on R.orderNo = r.orderNo

you can use OPENJSON AND CROSS APPLY
declare a list of order as JSON format
declare #orderNoList AS varchar(max) = '[1,2,3]'
and finally, use cross apply
SELECT *
FROM orderProperty OP
INNER JOIN Corder C ON OP.CorderId = C.id
CROSS APPLY (SELECT [value] FROM OPENJSON(#orderNoList) WHERE C.id = [value]) AS O

Related

Split string using delimiter that the SSRS passes - select multiple values from the parameter

I have this stored procedure where I am trying to select multiple values from the parameter Bkt. This works okay when I select one value from Bkt but Bkt has different values that I want to be able to select from in the SSRS.
For example in this screenshot you can see that there are CFI and DPR inside of Bkt. I want to be able to select both CFI and DPR in Bkt.
I need to keep the stored procedure so I am trying to split the delimited string that SSRS passes. I can't figure it out. I tried changing the delimiter by using an expression in the Parameters mapping section but I couldn't figure that out either. I am just so lost.
This is my Stored Procedure
#Press varchar(10),
#BKT varchar (10)
AS
BEGIN
SET NOCOUNT ON;
select * From (
Select FAC,SPEC_NEW.Tread_Code, Case When cosw.tread_code = spec_new.tread_code Then 'Scheduled' else 'Alternative' end AS Size, COUNT(Distinct loc.serial ) QTY ,
ROW_NUMBER() OVER (
PARTITION BY FAC, SPEC_NEW.Tread_Code
ORDER BY fac, Case When cosw.tread_code = spec_new.tread_code Then 'Scheduled' else 'Alternative' end DESC, SPEC_NEW.Tread_Code
) AS r_num
FROM [TireTrack].[dbo].[cos_work] cosw with (nolock)
Inner Join [SharedData].dbo.spec_master Spec with (nolock) On spec.spec=Cosw.SPEC
Inner Join [SharedData].dbo.spec_master SPEC_NEW with (nolock) On SPEC_NEW.ARTICLE=SPEC.article
Inner Join [DataWarehouse].[dbo].[Locator] LOC with (Nolock) ON LOC.SPEC=SPEC_NEW.SPEC
Where Cosw.FAC=#press and Loc.BKT = #bkt
GROUP BY FAC, cosw.Tread_Code, SPec_new.Tread_Code, Loc.Bkt
) as a
where r_num=1
order by FAC
END
I have another Stored Procedure where I am getting Press and Bkt
AS
BEGIN
SET NOCOUNT ON;
Select distinct FAC as Press, Loc.BKT as Bkt
FROM [TireTrack].[dbo].[cos_work], [DataWarehouse].[dbo].[Locator] LOC
END
I was using this link as a reference
Here is a conceptual example for you how to handle comma separated parameter to a stored procedure.
SQL
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, vehicleMake VARCHAR(20));
INSERT INTO #tbl (vehicleMake) VALUES
('Chevrolet'),
('Tesla'),
('Audi'),
('Nissan');
DECLARE #ParameterList VARCHAR(max) = '1,2';
-- Method #1
-- XML and XQuery
-- SQL Server 2008 onwards
DECLARE #separator CHAR(1) = ',';
;WITH rs AS
(
SELECT xmldata = TRY_CAST('<root><r>' +
REPLACE(#ParameterList, #separator, '</r><r>') + '</r></root>' AS XML)
)
SELECT tbl.*
FROM rs CROSS APPLY xmldata.nodes('/root/r/text()') AS t(c)
INNER JOIN #tbl AS tbl ON tbl.id = c.value('.','INT');
-- Method #2
-- STRING_SPLIT()
-- SQL Server 2016 onwards
SELECT tbl.*
FROM #tbl AS tbl INNER JOIN
STRING_SPLIT(#ParameterList, ',') AS ss
ON tbl.ID = ss.value;
Output
+----+-------------+
| ID | vehicleMake |
+----+-------------+
| 1 | Chevrolet |
| 2 | Tesla |
+----+-------------+

How do I use update multiple rows in a stored procedure

I get batches of inventory items to update and I would like to eliminate calling the stored procedure multiple times and instead call it once with multiple values. I have done similar in oracle with the parameters as an array trick. I would like to do something similar for SQL Server.
I have a comma separated list of Sku
I have a comma separated list of Quantity.
I have a comma separated list of StoreIds.
The standard update is
Update Inventory
set quantity = #Quantity
where sku = #Sku and StoreId = #StoreId;
Table definition
CREATE TABLE Inventory
(
[Sku] NVARCHAR(50) NOT NULL,
[Quantity] DECIMAL NULL DEFAULT 0.0,
[StoreId] INT NOT NULL
}
My bad attempt at doing this
ALTER PROCEDURE UpdateList
(#Sku varchar(max),
#Quantity varchar(max),
#StoreId varchar(max))
AS
BEGIN
DECLARE #n int = 0;
DECLARE #skuTable TABLE = SELECT CONVERT(value) FROM STRING_SPLIT(#Sku, ',');
DECLARE #quantityTable = SELECT CONVERT(value) FROM STRING_SPLIT(#Quantity, ',');
DECLARE #StoreIdTable = SELECT CONVERT(value) FROM STRING_SPLIT(#StoreId , ',');
WHILE #n < #skuTable.Count
BEGIN
UPDATE inventoryItem
SET Quantity = #quantityTable
WHERE Sku = #skuTable AND StoreId = #StoreIdTable;
SELECT #n = #n + 1;
END
END
I am open to using temp tables as parameters instead of comma separated. This is being called from an Entity Framework 6 context object from the front end system.
It's a bad practice to pass tabular values in this way.
Best solution is to pass it as a "user defined table type", if possible,
otherwise, it's better to get JSON/XML parameter
and then you can update your table like this:
--[ Parameters ]--
DECLARE #json AS NVARCHAR(MAX) = '[{"Sku":"A","Quantity":1.4,"StoreId":1},{"Sku":"B","Quantity":2.5,"StoreId":2},{"Sku":"C","Quantity":3.6,"StoreId":3}]';
--[ Bulk Update ]--
UPDATE inventoryItem
SET Quantity = I.Quantity
FROM inventoryItem AS T
JOIN OPENJSON(#json) WITH (Sku NVARCHAR(50), Quantity DECIMAL(5,1),StoreId INT) AS I
ON I.Sku = T.Sku
AND I.StoreId = T.StoreId
It's a bad practice to pass tabular values as varchar columns parameters,
but if you still want to go this way, here is a working code:
--[ Parameters ]--
DECLARE #Sku VARCHAR(max) = 'A,B,C',
#Quantity VARCHAR(max) = '1.4,2.5,3.6',
#StoreId VARCHAR(max) = '1,2,3'
--[ Converting VARCHAR Parameters to Table #Inventory ]--
DROP TABLE IF EXISTS #Sku
SELECT IDENTITY(int, 1,1) AS RowNum,
T.value
INTO #Sku
FROM STRING_SPLIT(#Sku, ',') AS T
DROP TABLE IF EXISTS #Quantity
SELECT IDENTITY(int, 1,1) AS RowNum,
T.value
INTO #Quantity
FROM STRING_SPLIT(#Quantity, ',') AS T
DROP TABLE IF EXISTS #StoreId
SELECT IDENTITY(int, 1,1) AS RowNum,
T.value
INTO #StoreId
FROM STRING_SPLIT(#StoreId, ',') AS T
DROP TABLE IF EXISTS #Inventory
SELECT Sku.value AS Sku,
Quantity.value AS Quantity,
StoreId.value AS StoreId
INTO #Inventory
FROM #Sku AS Sku
JOIN #Quantity AS Quantity ON Quantity.RowNum = Sku.RowNum
JOIN #StoreId AS StoreId ON StoreId.RowNum = Sku.RowNum
--[ Bulk Update ]--
UPDATE inventoryItem
SET Quantity = I.Quantity
FROM inventoryItem AS T
JOIN #Inventory AS I
ON I.Sku = T.Sku
AND I.StoreId = T.StoreId
The above answers are correct for updates and answered my question. But I wanted to add the insert here as I am sure someone will be looking for both. Maybe I will come back an make a new question and answer it myself.
I think the JSON version is best for my issue because I am doing entity framework and serializing an object to JSON is a trivial task. The basic process is to create an inline temp table from the json string. Calling out the objects via a simple dot notation string. I would suggest making the object passed in as simple as possible and preferably one level of properties.
create or alter Procedure bulkInventoryInsert( #json AS NVARCHAR(MAX))
AS
BEGIN
INSERT into inventory
SELECT Sku, Quantity, StoreId FROM
OPENJSON(#json)
WITH(Sku varchar(200) '$.Sku',
Quantity decimal(5,1) '$.Quantity',
StoreId INT '$.StoreId');
END
DECLARE #json AS NVARCHAR(MAX) = '[{"Sku":"A","Quantity":1.4,"StoreId":2},{"Sku":"B","Quantity":2.5,"StoreId":3},{"Sku":"C","Quantity":3.6,"StoreId":2}]';
EXECUTE bulkInventoryInsert #json;
The key part to recognize is this section here:
SELECT Sku, Quantity, StoreId FROM
OPENJSON(#json)
WITH(Sku varchar(200) '$.Sku',
Quantity decimal(5,1) '$.Quantity',
StoreId INT '$.StoreId');
This is creating a temp table with columns that match the table that it will be inserted into. The "WITH" portion specifies the column name, type, and where in the Json string to get the value.
I hope this will help. Maybe when I get time I will do a question and answer for this.

TSQL Where clause based on temp table data

I have a straight forward SQL query that I am working with and trying to figure out the best way to approach the where clause.
Essentially, there are two temp tables created and if there is data in the XML string passed to the stored procedure, those tables are populated.
My where clause needs to check these temp tables for data, and if there is no data, it ignores them like they are not there and fetches all data.
-- Create temp tables to hold our XML filter criteria
DECLARE #users AS TABLE (QID VARCHAR(10))
DECLARE #dls AS TABLE (dlName VARCHAR(50))
-- Insert our XML filters
IF #xml.exist('/root/data/users') > 0
BEGIN
INSERT INTO #users( QID )
SELECT ParamValues.x1.value('QID[1]', 'varchar(10)')
FROM #xml.nodes('/root/data/users/user') AS ParamValues(x1)
END
IF #xml.exist('/root/data/dls') > 0
BEGIN
INSERT INTO #dls( dlName )
SELECT ParamValues.x1.value('dlName[1]', 'varchar(50)')
FROM #xml.nodes('/root/data/dld/dl') AS ParamValues(x1)
END
-- Fetch our document details based on the XML provided
SELECT d.documentID ,
d.sopID ,
d.documentName ,
d.folderLocation ,
d.userGroup ,
d.notes
FROM dbo.Documents AS d
LEFT JOIN dbo.DocumentContacts AS dc
ON dc.documentID = d.documentID
LEFT JOIN dbo.DocumentContactsDLs AS dl
ON dl.documentID = d.documentID
-- How can I make these two logic checks work only if there is data, otherwise, include everything.
WHERE dc.QID IN (SELECT QID FROM #users)
AND dl.DL IN (SELECT dlName FROM #dls)
FOR XML PATH ('data'), ELEMENTS, TYPE, ROOT('root');
In the query above, I am trying to used the data in the temp tables only if there is data in them, otherwise, it needs to act like that where statement isn't there for that specific value and include records regardless.
Example: If only #users had data, it would ignore AND dl.DL IN (SELECT dlName FROM #dls) and get everything, regardless of what was in the DL column on those joined records.
Use NOT EXISTS to check the existence of any record in variable table. Here is one way
WHERE ( dc.QID IN (SELECT QID FROM #users)
OR NOT EXISTS (SELECT 1 FROM #users) )
AND ( dl.DL IN (SELECT dlName FROM #dls)
OR NOT EXISTS (SELECT 1 FROM #dls) )
Try this. But please note that I did not get a chance to test it properly and I believe that you want to check the values in #users first and if there is no record existing in that table, then you want to check with the entries in #dls. Also if there are no entries in both of these tables, then you want to skip both the tables.
DECLARE #fl bit = 0
SELECT #fl = CASE WHEN EXISTS (SELECT 1 FROM #users) THEN
1
WHEN EXISTS (SELECT 1 FROM #dls) THEN
2
ELSE
0
END
WHERE ( (dc.QID IN (SELECT QID FROM #users) AND #fl = 1)
OR
(dl.DL IN (SELECT dlName FROM #dls) AND #fl = 2)
OR (1=1 AND #fl = 0)
)

Retrieve values for multiple ids given in the order

I am having a stored procedure which uses CSV to accept multiple IDs. I want to retrieve the values from the table in the order of the Ids given. The stored procedure I am currently using is
CREATE procedure [dbo].[Proc1]
#Userid VARCHAR(MAX)=NULL
as
begin
set nocount on;
DECLARE #Useridord TABLE (Userid VARCHAR(MAX),Position int identity(1,1));
INSERT INTO #Useridord
SELECT item
FROM [dbo].[Split] (#Userid, ',')
select * from User where User.UserId IN (select TOP 100 Percent Userid from #Useridord ORDER BY Position)
end
GO
What I am trying to do is from csv I am inserting the values into temporary table adding the order value as position. As I use Orderby Position, it is only implemented in the inner select. But the Output is given as the order in the table. I know to use an orderby in the outer select statement but I don't know the correct syntax to execute it. Can anyone help me?
How about using INNER JOIN?
SELECT
u.*
FROM User u
INNER JOIN #Useridord uo
ON uo.Userid = u.UserId
ORDER BY uo.Position
I believe that you should guaranty position inside function like:
create function fnSplit(#users varchar(100), #del char(10))
returns #t table(userid int, pos int) as
begin
insert into #t values(3,1),(2,2),(1,3)
return
end
Then you can join directly on function result like:
Select u.* from users u
join fnSplit('','') s on u.id = s.userid
order by s.pos
Here is fiddle http://sqlfiddle.com/#!6/a8acc/4

coalesce two records into one

I have a table that stores two values; 'total' and 'owing' for each customer. Data is uploaded to the table using two files, one that brings in 'total' and the other brings in 'owing'. This means I have two records for each customerID:
customerID:--------Total:--------- Owing:
1234---------------- 1000----------NULL
1234-----------------NULL-----------200
I want to write a stored procedure that merges the two records together:
customerID:--------Total:--------- Owing:
1234---------------- 1000----------200
I have seen examples using COALESCE so put together something like this:
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--Variable declarations
DECLARE #customer_id varchar(20)
DECLARE #total decimal(15,8)
DECLARE #owing decimal(15,8)
DECLARE #customer_name_date varchar(255)
DECLARE #organisation varchar(4)
DECLARE #country_code varchar(2)
DECLARE #created_date datetime
--Other Variables
DECLARE #totals_staging_id int
--Get the id of the first row in the staging table
SELECT #totals_staging_id = MIN(totals_staging_id)
from TOTALS_STAGING
--iterate through the staging table
WHILE #totals_staging_id is not null
BEGIN
update TOTALS_STAGING
SET
total = coalesce(#total, total),
owing = coalesce(#owing, owing)
where totals_staging_id = #totals_staging_id
END
END
Any Ideas?
SELECT t1.customerId, t1.total, t2.owing FROM test t1 JOIN test t2 ON ( t1.customerId = t2.customerId) WHERE t1.total IS NOT NULL AND t2.owing IS NOT NULL
Wondering why aren't you just using UPDATE on a second file execution?
Except for COUNT, aggregate functions ignore null values. Aggregate
functions are frequently used with the GROUP BY clause of the SELECT
statement. MSDN
So you don't need to worry about null values with summing. Following will give your merging records together. Fiddle-demo
select customerId,
sum(Total) Total,
sum(Owing) Owing
from T
Group by customerId
Try this :
CREATE TABLE #Temp
(
CustomerId int,
Total int,
Owing int
)
insert into #Temp
values (1024,100,null),(1024,null,200),(1025,10,null)
Create Table #Final
(
CustomerId int,
Total int,
Owing int
)
insert into #Final
values (1025,100,50)
MERGE #Final AS F
USING
(SELECT customerid,sum(Total) Total,sum(owing) owing FROM #Temp
group by #Temp.customerid
) AS a
ON (F.customerid = a.customerid)
WHEN MATCHED THEN UPDATE SET F.Total = F.Total + isnull(a.Total,0)
,F.Owing = F.Owing + isnull(a.Owing,0)
WHEN NOT MATCHED THEN
INSERT (CustomerId,Total,Owing)
VALUES (a.customerid,a.Total,a.owing);
select * from #Final
drop table #Temp
drop table #Final
This should work:
SELECT CustomerID,
COALESCE(total1, total2) AS Total,
COALESCE(owing1, owing2) AS Owing
FROM
(SELECT row1.CustomerID AS CustomerID,
row1.Total AS total1,
row2.Total AS total2,
row1.Owing AS owing1,
row2.Owing AS owing2
FROM YourTable row1 INNER JOIN YourTable row2 ON row1.CustomerID = row2.CustomerID
WHERE row1.Total IS NULL AND row2.Total IS NOT NULL) temp
--Note: Alter the WHERE clause as necessary to ensure row1 and row2 are unique.
...but note that you'll need some mechanism to ensure row1 and row2 are unique. My WHERE clause is an example based on the data you provided. You'll need to tweak this to add something more specific to your business rules.

Resources