SQL parameter insert in temp table - sql-server

I have ssrs report which receives multivalued parameter like we can check USA,India,Germany from the checkboxes
Parameter name is #country
What I need is I need to store the parameter in table in below format
Create table #tempcountry
(Name varchar(20))
Insert into #tempcountry values (#country)
What I basically need is table has to store values like
Name
USA
India
Germany
This is my requirement I am struck here can anyone help me out on this??

In this case the parameter must be a table typed one, passed in READ ONLY mode and having all the data inserted before in a table variable.
Example :
CREATE TYPE TYP_COUNTRIES AS TABLE (COUNTRY_NAME VARCHAR(100));
GO
The query report must be base on a function or a procedure :
CREATE FUNCTION dbo.F_T_MyQuery (#CTRY TYP_COUNTRIES READONLY)
RETURNS TABLE
AS
RETURN (SELECT *
FROM MyTable
WHERE Country_name IN (SELECT * FROM #CTRY);
GO
To use it :
DECLARE #Countries TYP_COUNTRIES;
INSERT INTO #Countries VALUES ('spain', 'italy', 'france')
SELECT *
FROM dbo.F_T_MyQuery (#Countries);

Related

Query table-per-month data without dynamic SQL

A vendor, in their infinite wisdom, has decided to split a holidays dataset by month into separate tables i.e.
holidayPlanner.dbo.holiday_201802
holidayPlanner.dbo.holiday_201803
holidayPlanner.dbo.holiday_201804
holidayPlanner.dbo.holiday_201805
These tables are generated by the system as soon as an employee requests a holiday in the month. I can write a query that uses dynamic SQL to create a UNION query that I can work with.
Is there any way this can be done without dynamic SQL? Ideally, so I can create a table-valued-function to get the results?
you could use a table function that returns as the result the values obtained in each of the tables to be identified (you could do a search by months)
I leave the following example:
CREATE FUNCTION [dbo].[ufnHolidays]()
RETURNS #holidays TABLE
(
id int
)
AS
BEGIN
/* example data
CREATE TABLE #holiday_201802 (id int)
CREATE TABLE #holiday_201803 (id int)
CREATE TABLE #holiday_201804 (id int)
INSERT #holiday_201802 VALUES (1)
INSERT #holiday_201803 VALUES (2)
INSERT #holiday_201804 VALUES (3)
*/
IF OBJECT_ID('tempdb.dbo.#holiday_201802') IS NOT NULL
INSERT #holidays
SELECT id FROM #holiday_201802
IF OBJECT_ID('tempdb.dbo.#holiday_201803') IS NOT NULL
INSERT #holidays
SELECT id FROM #holiday_201803
IF OBJECT_ID('tempdb.dbo.#holiday_201804') IS NOT NULL
INSERT #holidays
SELECT id FROM #holiday_201804
RETURN;
END;
GO

Multiple values in one parameter in a stored procedure

My stored procedure needs to insert multiple products in the [Order Details] table.
One OrderID = Many products.
I saw a code where you will create a temporary table like a list to store multiple values. But I don't know how to use it when the values is not pre-defined or is a user input.
--I want to input multiple ProductID in this temporary table
DECLARE #myOrders table (ID int)
INSERT INTO #myOrders ("")
--Then get data from the Products table and input the data in Order Details
INSERT INTO [Order Details] (ProductID)
SELECT ProductID From Products
WHERE ProductID IN (SELECT ID FROM #myOrders)
Sample pic of Order details
First you need to define type like following
CREATE TYPE ProductList AS TABLE
(
ProductId INT
)
GO
Then create your procedure like following
ALTER PROCEDURE USP_TEST_PROC (#OrderId INT,#Produt_Id_List ProductList READONLY)
AS
BEGIN
DECLARE #OrderDetails TABLE(OrderId INT, ProductID INT,UnitPrice DECIMAL(18,2),Quantity INT, Discount DECIMAL(18,2))
DECLARE #Products TABLE(ProductID INT,UnitPrice DECIMAL(18,2),Quantity INT, Discount DECIMAL(18,2))
INSERT INTO #OrderDetails (OrderId, ProductID,UnitPrice,Quantity, Discount)
SELECT #OrderId, ProductID,UnitPrice,Quantity,Discount FROM #Products WHERE ProductID IN (SELECT ProductId FROM #Produt_Id_List)
SELECT * FROM #OrderDetails
END
Then prepare table variable to put values like following
DECLARE #PList ProductList;
INSERT #PList VALUES (1),(2),(3)
Finally call procedure
EXEC USP_TEST_PROC 100,#PList
Thanks
Stored procedures accept table inputs in the form of table values parameters. Look at the documentation here
https://learn.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters-database-engine
This includes an example also on how to invoke such a stored procedure.
You can directly use the TVP similar to a table variable (#myOrders in you sample) in the stored procedure body.
If you are looking to also call that stored procedure from ADO.NET, below article has a good description.
https://www.codeproject.com/Articles/39161/C-and-Table-Value-Parameters

Sharing data between stored procedures

I have a stored procedure called dbo.Match.
It looks like this :
CREATE Procedure [dbo].[MATCH]
#parameterFromUser nvarchar(30),
#checkbool int
As
Begin
--SOME CODE
select RowId,
PercentMatch
from #Matches
End
This procedure is being called from another stored procedure :
CREATE Procedure MatchMotherFirstName
#MotherFN nvarchar(20) , #checkbool int
As begin
SELECT #constVal = FunctionWeight
FROM dbo.FunctionWeights
WHERE FunctionWeights.FunctionId = 20;
/*
Some code to execute `dbo.Match` procedure in above procedure called `MatchMotherFirstName` , retrieve `RowNumber` and `PercentMatch`,
Insert into #Temp in their respective fields , and calculate `PercentMatch * constVal`,
and insert in corresponding column called `percentage` in `#Temp`
*/
End
I need to execute dbo.Match stored procedure in above procedure, retrieve RowID and PecrntMatch value, #constval value we have above, multiply #constVal and percentmatch and store it in Percentage column of #Temp and insert results from dbo.Match procedure in a temporary table. dbo.Match returns only RowId and PercentMatch.
Structure of Temporary table:
create table #Temp
(
Rownumber int not null,
ValFromUser nvarchar(30),
ColumnName nvarchar(30),
ValFromFunc decimal(18, 4),
FuncWeight decimal(18, 4), -- #constVal here
Percentage decimal(18, 4) not null, -- calculated value here i.e (FuncWeight * ValFromFunc)
);
In #Temp, I need to insert the value of #constVal as well as calculate a column and insert i.e PercentMatch * contVal for rows inserted in this execution call only.
How can I do this in above procedure in most efficient way ?
Edit : For purpose of clarity , here is what I was doing if dbo.Match was a function and not a procedure:
if #MotherFN is not null
begin
SELECT #constVal = FunctionWeight
FROM dbo.FunctionWeights
WHERE FunctionWeights.FunctionId = 20;
INSERT INTO #Temp2
(RowNumber,ValFromUser,ColumnName,ValFromFunc,FuncWeight,percentage)
SELECT RowId,
#MotherFN ,
'mothersfirstname'
,PercentMatch,
#constVal,
PercentMatch * #constVal
FROM dbo.MatchMatch(#MotherFN, 0)
end
Like I can retrieve value of Percentmatch, #constval and multiply them both to insert in #Temp , how I may do this while I execute the dbo.Match procedure instead of calling dbo.Match function ?
You have several options, ranging from incredibly easy to overly complicated. The easiest (and most efficient) ways of doing what you describe are:
Don't do it: just include that calculation in the query. Why does it need to be in the table definition?
Add a computed column the temp table when it is created. This requires that you also include a field to store the "Constant Value" so that it can be referenced by the computed column. If the calculation is somewhat expensive and/or there will be lots of rows and frequently selected from (and possibly used in WHERE and/or ORDER BY clauses), then you can make the computed column PERSISTED so that it is calculated upon INSERT and any UPDATE that updates the fields referenced in the computed column.
Add a computed column the temp table after the table has been created. This allows for embedding the "Constant Value" into the computed column so that there is no need for a [ConstantValue] column. If the calculation is somewhat expensive and/or there will be lots of rows and frequently selected from (and possibly used in WHERE and/or ORDER BY clauses), then you can make the computed column PERSISTED so that it is calculated upon INSERT and any UPDATE that updates the fields referenced in the computed column.
P.S. Just in case you find yourself asking "why not just create the temp table dynamically in one step instead of two steps?": a local temporary table created in Dynamic SQL will cease to exist after the EXEC of that Dynamic SQL. A global temp table will survive the execution of the Dynamic SQL, but then the table name is shared across all sessions so another session executing this code at the same time would error on the name conflict. In that case you would need to generate a GUID via NEWID() to use as the global temp table name and concatenate that value as well into the Dynamic SQL, but then you are stuck being required to use Dynamic SQL for all references to the global temp table (including for the INSERT...EXEC) and that is just more work for no benefit.
Test Setup
IF (OBJECT_ID(N'tempdb..#InnerProc') IS NOT NULL)
BEGIN
DROP PROCEDURE #InnerProc;
END;
GO
IF (OBJECT_ID(N'tempdb..#TempResults1') IS NOT NULL)
BEGIN
DROP TABLE #TempResults1;
END;
IF (OBJECT_ID(N'tempdb..#TempResults2') IS NOT NULL)
BEGIN
DROP TABLE #TempResults2;
END;
IF (OBJECT_ID(N'tempdb..#TempResults3') IS NOT NULL)
BEGIN
DROP TABLE #TempResults3;
END;
GO
CREATE PROCEDURE #InnerProc
AS
SET NOCOUNT ON;
SELECT TOP 20 so.[object_id], so.[modify_date]
FROM [master].[sys].[objects] so
ORDER BY so.[modify_date] DESC;
GO
Option 1
CREATE TABLE #TempResults1
(
[ObjectId] INT NOT NULL,
[ModifyDate] DATETIME NOT NULL
);
DECLARE #ConstantValue1 INT;
SET #ConstantValue1 = 13;
INSERT INTO #TempResults1 ([ObjectId], [ModifyDate])
EXEC #InnerProc;
SELECT 1 AS [Test], *, DATEADD(DAY, #ConstantValue1, [ModifyDate]) AS [SomeCalculation]
FROM #TempResults1;
Option 2
CREATE TABLE #TempResults2
(
[ObjectId] INT NOT NULL,
[ModifyDate] DATETIME NOT NULL,
[ConstantValue] INT NULL, -- will be added via UPDATE
[SomeCalculation] AS (DATEADD(DAY, [ConstantValue], [ModifyDate])) -- PERSISTED ??
);
INSERT INTO #TempResults2 ([ObjectId], [ModifyDate])
EXEC #InnerProc;
SELECT 2 AS [Test], * FROM #TempResults2;
UPDATE #TempResults2
SET [ConstantValue] = 13;
SELECT 2 AS [Test], * FROM #TempResults2;
Option 3
DECLARE #ConstantValue3 INT;
SET #ConstantValue3 = 13;
CREATE TABLE #TempResults3
(
[ObjectId] INT NOT NULL,
[ModifyDate] DATETIME NOT NULL
);
INSERT INTO #TempResults3 ([ObjectId], [ModifyDate])
EXEC #InnerProc;
SELECT 3 AS [Test], * FROM #TempResults3;
-- The next 3 lines could be done just after the CREATE TABLE and before the INSERT,
-- but doing it now allows for seeing the "before" and "after" with the data.
DECLARE #SQL NVARCHAR(MAX);
SET #SQL = N'ALTER TABLE #TempResults3 ADD [SomeCalculation] AS (DATEADD(DAY, '
+ CONVERT(NVARCHAR(10), #ConstantValue3) + N', [ModifyDate])); --PERSISTED';
EXEC (#SQL);
SELECT 3 AS [Test], * FROM #TempResults3;
Well, in general terms there is no value on creating complex logics if you just need to do simple stuffs. In the scenario that you described I would tend to think the best approach would be the use of phisical table that can be accessed any time by either the dbo.Match and dbo.MatchMotherFirstName procedures. If you don't want to leave it in the database after logic execution, use the CREATE/DROP sentences to CREATE/DROP the table as per your needs.
You have 3 Easy enough options. One has a decent sized performance hit, One requires a config update on the server and One requires a change to the match stored procedure.
Option 1
In the MatchMotherFirstName procedure declare a table for the Match results.
CREATE TABLE #tmpMatchResults (Col1 , Col2....)
Insert into #tmpMatchResults
EXEC [dbo].[MATCH]
This has a performance hit, but it works without any changes to the Match proc code or server config. If you only expect very few lines, this will work just fine
Option 2
Use OpenRowSet or OpenQuery
Select * FROM OPENROWSET(connection,'Exec database.dbo.MATCH')
This requires a config change to allow data access
Option 3
Update the MATCH Stored Procedure to push the results to a temp table
CREATE Procedure [dbo].[MATCH]
--SOME CODE
select RowId, PercentMatch from #tmpMatches
Be sure NOT to drop the temp table at the end of the proc
Then In your MatchMotherFirstName procedure, while the session is active, you can call the proc
EXEC dbo.MATCH #param
and the result set with
SELECT * FROM #tmpMatches
Some people would argue that you should clean up (drop table) the temp table at the end of the MATCH proc call. You can include a parameter in the MATCH proc to persist results or do table cleanup.

One Stored Procedure to insert one table and retrieve ID and insert multiple tables

I have 3 tables and columns. ex: Green (userID, Name, Address), Red (userID, TemproraryAddress), Blue (userID, WorkPlace).
I want to insert data into Green while userID is auto incremented int and unique id. After inserting those details want to retrieve userID and insert into Red and Blue Tables in one stored procedure
Note: This example table is to show a sample scenario, not for real world usage...
SQL has a very nice OUTPUT feature.
In this scenario, specify your identity column with an auto incrementing int or default value (such as newid()), do the insert and then use the output as input for your other input queries.
The output table has all the values just inserted and can be queried just as any other table, with the difference being it has your new value immediately available after commit.
INSERT INTO
tblInsertedID(ID)
SELECT * FROM
(INSERT
INTO tblDir(Dir)
OUTPUT inserted.ID
VALUES ('C:\NewDir\')
) InnerTable
DECLARE #userID INT;
INSERT dbo.Green(Name, Address) SELECT #name, #address;
SELECT #userID = SCOPE_IDENTITY();
INSERT dbo.Red(UserID, TemporaryAddress) SELECT #userID, #something;
INSERT dbo.Blue(UserID, WorkPlace) SELECT #userID, #something_else;
Use SCOPE_IDENTITY() to insert value in a variable for user is and then use this variable value in Red/Blue tables.

SELECT INTO a table variable in T-SQL

Got a complex SELECT query, from which I would like to insert all rows into a table variable, but T-SQL doesn't allow it.
Along the same lines, you cannot use a table variable with SELECT INTO or INSERT EXEC queries.
http://odetocode.com/Articles/365.aspx
Short example:
declare #userData TABLE(
name varchar(30) NOT NULL,
oldlocation varchar(30) NOT NULL
)
SELECT name, location
INTO #userData
FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30
The data in the table variable would be later used to insert/update it back into different tables (mostly copy of the same data with minor updates). The goal of this would be to simply make the script a bit more readable and more easily customisable than doing the SELECT INTO directly into the right tables.
Performance is not an issue, as the rowcount is fairly small and it's only manually run when needed.
...or just tell me if I'm doing it all wrong.
Try something like this:
DECLARE #userData TABLE(
name varchar(30) NOT NULL,
oldlocation varchar(30) NOT NULL
);
INSERT INTO #userData (name, oldlocation)
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;
The purpose of SELECT INTO is (per the docs, my emphasis)
To create a new table from values in another table
But you already have a target table! So what you want is
The INSERT statement adds one or more new rows to a table
You can specify the data values in the
following ways:
...
By using a SELECT subquery to specify
the data values for one or more rows,
such as:
INSERT INTO MyTable
(PriKey, Description)
SELECT ForeignKey, Description
FROM SomeView
And in this syntax, it's allowed for MyTable to be a table variable.
You can also use common table expressions to store temporary datasets. They are more elegant and adhoc friendly:
WITH userData (name, oldlocation)
AS
(
SELECT name, location
FROM myTable INNER JOIN
otherTable ON ...
WHERE age>30
)
SELECT *
FROM userData -- you can also reuse the recordset in subqueries and joins
You could try using temporary tables...if you are not doing it from an application. (It may be ok to run this manually)
SELECT name, location INTO #userData FROM myTable
INNER JOIN otherTable ON ...
WHERE age>30
You skip the effort to declare the table that way...
Helps for adhoc queries...This creates a local temp table which wont be visible to other sessions unless you are in the same session. Maybe a problem if you are running query from an app.
if you require it to running on an app, use variables declared this way :
DECLARE #userData TABLE(
name varchar(30) NOT NULL,
oldlocation varchar(30) NOT NULL
);
INSERT INTO #userData
SELECT name, location FROM myTable
INNER JOIN otherTable ON ...
WHERE age > 30;
Edit: as many of you mentioned updated visibility to session from connection. Creating temp tables is not an option for web applications, as sessions can be reused, stick to temp variables in those cases
Try to use INSERT instead of SELECT INTO:
DECLARE #UserData TABLE(
name varchar(30) NOT NULL,
oldlocation varchar(30) NOT NULL
)
INSERT #UserData
SELECT name, oldlocation
First create a temp table :
Step 1:
create table #tblOm_Temp (
Name varchar(100),
Age Int ,
RollNumber bigint
)
**Step 2: ** Insert Some value in Temp table .
insert into #tblom_temp values('Om Pandey',102,1347)
Step 3: Declare a table Variable to hold temp table data.
declare #tblOm_Variable table(
Name Varchar(100),
Age int,
RollNumber bigint
)
Step 4: select value from temp table and insert into table variable.
insert into #tblOm_Variable select * from #tblom_temp
Finally value is inserted from a temp table to Table variable
Step 5: Can Check inserted value in table variable.
select * from #tblOm_Variable
OK, Now with enough effort i am able to insert into #table using the below :
INSERT #TempWithheldTable SELECT
a.SuspendedReason,
a.SuspendedNotes,
a.SuspendedBy ,
a.ReasonCode FROM OPENROWSET( BULK 'C:\DataBases\WithHeld.csv', FORMATFILE =
N'C:\DataBases\Format.txt',
ERRORFILE=N'C:\Temp\MovieLensRatings.txt'
) AS a;
The main thing here is selecting columns to insert .
One reason to use SELECT INTO is that it allows you to use IDENTITY:
SELECT IDENTITY(INT,1,1) AS Id, name
INTO #MyTable
FROM (SELECT name FROM AnotherTable) AS t
This would not work with a table variable, which is too bad...

Resources