I have problem where I have 3 tables with date single date column. I need to assign one of these to variable to use as a filter later on in my query. Example will clear this one.
CREATE TABLE #missing_days (trade_date DATE)
INSERT INTO #missing_days (trade_date) VALUES ('2022-04-10'), ('2022-04-09')
CREATE TABLE #all_days (trade_date DATE)
INSERT INTO #all_days (trade_date) VALUES ('2022-04-12'), ('2022-04-11'), ('2022-04-10'), ('2022-04-09')
CREATE TABLE #one_day (trade_date DATE)
INSERT INTO #one_day (trade_date) VALUES ('2022-04-12')
Now I have declared parameter to tell me what I would like to run
DECLARE #what_to_run VARCHAR(10) = 'ALL' --ALL run all dates, MISSING run only missing dates, ONE run only one day
Now depending what value #what_to_run has I need to use correct table in simple WHERE clause later on.
My idea was something like this:
DECLARE #Dates_to_run TABLE
(trade_date DATE)
SET #Dates_to_run = CASE WHEN #what_to_run = 'ALL' THEN (SELECT * FROM #all_days)
WHEN #what_to_run = 'MISSING' THEN (SELECT * FROM #missing_days)
ELSE (SELECT * FROM #one_day) END
but haven't had much success with it.
Expected result would be that when #what_to_run = 'ALL' I would have table with those dates from #all_days to use in simple query such as
SELECT *
FROM dt
WHERE trade_date IN (SELECT trade_date FROM #Dates_to_run)
I'm using SQL Server.
You must insert into your table variable, maybe this is what you want ?
declare #what_to_run varchar(20) = 'all'
DECLARE #Dates_to_run TABLE (trade_date DATE)
if #what_to_run = 'ALL'
begin
insert into #Dates_to_run
select * from #all_days
end
else if #what_to_run = 'MISSING'
begin
insert into #Dates_to_run
SELECT * FROM #missing_days
end
else
begin
insert into #Dates_to_run
SELECT * FROM #one_day
end
select * from #Dates_to_run
Note that this will only work if the table #all_days, #missing_days and #one_day only have one column and it must be of the datatype DATE
You can use Union and WHERE condition and Subquery by changing variable to get your desired result
DECLARE #what_to_run VARCHAR(10) = 'ALL'
DECLARE #Dates_to_run TABLE
(trade_date DATE)
INSERT INTO #Dates_to_run
SELECT trade_date FROM
(SELECT *,'ALL' AS V FROM #all_days
UNION
SELECT *,'MISSING' AS V FROM #missing_days
UNION
SELECT *,'Oneday' AS V FROM #one_day) T
WHERE #what_to_run=T.V
Related
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.
TBMEMBER columns
id,name,employeeno,userno,amount1,amount2,type,status
TBDEDUCT columns
id,idno,employeeno,date,name,amount,status
TBITEMS
id,employeeno,userno,itemname,amount,status
SYNTAX
DECLARE memberlist CURSOR FOR SELECT id from TBMEMBER a where Status ='A' and Type = 'R'
and employeeno not in (select EmployeeNo from TBRESIGN where (txstatus='5' OR txstatus ='7' or txstatus='4') and EmployeeNo = a.EmployeeNo)
DECLARE #itemamt as decimal
select top 0 *
into #tempmember
from TBMEMBER
OPEN memberlist
FETCH NEXT FROM memberlist
INTO #id
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #tempmember SELECT * FROM TBMEMBER where id =#id
select #itemamt = sum(amount) from TBITEMS where employeeno = #tempmember.employeeno and status = '9'
insert into #TBDEDUCT values (#tempmember.userno,#tempmember.EmployeeNo,getdate(),#tempmember.name,#tempmember.amount1,'P')
insert into #TBDEDUCT values (#tempmember.userno,#tempmember.EmployeeNo,getdate(),#tempmember.name,#tempmember.amount2,'P')
insert into #TBDEDUCT values (#tempmember.userno,#tempmember.EmployeeNo,getdate(),#tempmember.name,#tempmember.#itemamt,'P')
DELETE FROM #tempmember
END
I'm trying to insert values into tbdeduct from temptable but it gives me an error:
The multi-part identifier "#tempmember.SLAIdNo" could not be bound.
You need to declare variables for the other columns and assign them values in the FETCH statement. Use those variables in the INSERT statement instead of using the table.columname. However, you do not need to use a CURSOR for this. Here is one way:
WITH CteTBMember AS( -- Rows from your CURSOR
SELECT tm.*
FROM TBMEMBER tm
WHERE
tm.Status ='A'
AND tm.Type = 'R'
AND tm.employeeno NOT IN (
SELECT EmployeeNo
FROM TBRESIGN
WHERE
(txstatus='5' OR txstatus ='7' or txstatus='4')
AND EmployeeNo = a.EmployeeNo
)
)
INSERT INTO #TBDEDUCT
SELECT
tm.idNo,
tm.EmployeeNo,
GETDATE(),
tm.name,
x.amount,
'P'
FROM CTeTbMember tm
CROSS APPLY( -- 3 types of amount to be inserted
SELECT tm.amount1 UNION ALL
SELECT tm.amount2 UNION ALL
SELECT SUM(amount)
FROM TBITEMS ti
WHERE
ti.employeeno = tm.employeeno
AND ti.status = '9'
) x(amount);
I have an SSIS package which will first run my sp_doSomething. This stored procedure will select data from several different tables and join them for possible storage into dbo.someTable. But I only want that IF the select is > 1 row of selected data.
I want to then have a precedence restraint that looks at the amount of rows my stored procedure returned.
If my row count > 1, then I want to take the results of the stored procedure and insert them into one of my tables.
Otherwise, I will record an error/send an email, or whatever.
I really don't want to run this stored procedure more then once, but that is the only way I could think to do it (Run it, count the rows. Then, run it again and insert the result).
I'm a complete TSQL/SSIS newb. So I'm sorry if this question is trivial.
I can't find a good answer anywhere.
Create a variable with Package Scope of type Int32 and name rowcount.
Data Flow
Control Flow
you can try this
declare #tableVar table(col1 varchar(100))
declare #Counter int
insert into #tableVar(col1) exec CompanyNames
set #Counter = (select count(*) from #tableVar)
insert into Anytable(col) Values (#counter)
Within the Stored Proc, write the results to a #Temp. Then Select Count(*) from the #Temp, into a variable.
Select #intRows = Count(*) from myTempResults
Then evaluate the value of #intRows.
If #intRows > 1 BEGIN
Insert Into dbo.SomeTable
Select * from #Temp
End
Will a #temp table work for you?
IF OBJECT_ID('tempdb..#Holder') IS NOT NULL
begin
drop table #Holder
end
CREATE TABLE #Holder
(ID INT )
declare #MyRowCount int
declare #MyTotalCount int = 0
/* simulate your insert, you would read from your real table(s) here */
INSERT INTO #HOLDER (ID)
select 1 union all select 2 union all select 3 union all select 4
Select #MyRowCount = ##ROWCOUNT, #MyTotalCount = #MyTotalCount + #MyRowCount
Select 'TheMagicValue1' = #MyRowCount, 'TheMagicTotal' = #MyTotalCount
INSERT INTO #HOLDER (ID)
select 5 union all select 6 union all select 7 union all select 8
/* you will note that I am NOT doing a count(*) here... which is another strain on the procedure */
Select #MyRowCount = ##ROWCOUNT, #MyTotalCount = #MyTotalCount + #MyRowCount
Select 'TheMagicValue1' = #MyRowCount, 'TheMagicTotal' = #MyTotalCount
/* Optional index if needed */
CREATE INDEX IDX_TempHolder_ID ON #Holder (ID)
/* CREATE CLUSTERED INDEX IDX_TempHolder_ID ON #Holder (ID) */
if #MyTotalCount > 0
BEGIN
Select 'Put your INSERT statement here'
END
/* this will return the data to the report */
Select ID from #HOLDER
IF OBJECT_ID('tempdb..#Holder') IS NOT NULL
begin
drop table #Holder
end
I am trying to acheive like this at the end of the procedure i need all the rows in
one temp table
How can I accomplish this
if #i > 1
begin
select * from into #tempTbl1 from payments
where method = 'test1'
end
else
begin
select * from into #tempTbl2 from payments
where method = 'test1'
end
insert into #tempTbl1 select * from #tempTbl2
select * from #tempTbl1
despite the previous logic issue, to simplely get all rows from both temp tables , use UNION:
select * from #tempTbl1
UNION ALL
SELECT * from #tempTbl2
The problem you have here is that based on your IF/ELSE you will never had both tables. Your final INSERT INTO requires that both tables exist. You may need to create the objects before hand in the stored proc before you try to populate, then insert into the tables.
This also begs the question, if you're going to later insert everything in #tempTbl1 anyways, which is created in the SELECT INTO statement, why have the #tempTbl2 in the first place?
create procedure dbo.testing
(#i int)
AS
if #i > 1
begin
print 'In condition 1'
select *
into #tempTbl1
from payments
where method = 'test1'
end
else
begin
print 'In condition 2'
select *
into #tempTbl2
from payments
where method = 'test1'
end
print 'Made it out of the if else'
insert into #tempTbl1
select *
from #tempTbl2
-- Never gets this far...
print 'In the final select'
select *
from #tempTbl1
If you're committed to this method, then you may need to check to see if the table exists:
IF EXISTS (SELECT * FROM tempdb.sys.objects WHERE object_id = OBJECT_ID(N'tempdb.dbo.#tempTbl1') AND type in (N'U'))
print 'Table is there'
Update based on comments
Based on your comments, this will work. The SELECT...INTO statement you originally posted lets you create a table based on the data types of the columns your selecting from, but the destination table can't already exist. If you define the structure you're going to insert into beforehand, you can have the two conditions evaluate and end up with a single table as the result.
(Note - my "payments" table only had two columns, "method" and "col2". You would want to specify the columns you need in the CREATE TABLE and the SELECT)
create procedure dbo.testing
(#i int)
AS
create table #tempTbl1
(method varchar(10)
, col2 int)
if #i > 1
begin
insert into dbo.#tempTbl1
select method, col2
from payments
where method = 'test1'
end
else
begin
insert into dbo.#tempTbl1
select method, col2
from payments
where method = 'test1'
end
select *
from #tempTbl1
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.