SQL Server JOIN table as variable - sql-server

I am trying to do the following where the #POtable variable and #ProdID will be passed to the stored procedure. The #POtable name shall be one of the Order table in the database, that I shall select randomly. I know that the following statement is incorrect, what would be the right syntax?
Select *
From Products
Join #POtable On Products.ProdID = #POtable.ProdID
Where ProdID = #ProdID
Thanks for helping.

As said in comments,you can't use variables in Tablenames,column names,Databasename,function names...
To use variables you have to use dynamic SQL..
Declare #potable Nvarchar(200);
Set #potable='sometablename';
Declare #SQL nVarchar(max)
Set #SQL='
Select *
From Products p
Join '+QUOTENAME(#POtable)+' po On p.ProdID = po.ProdID
Where p.ProdID = '+#ProdID;
Exec(#SQL)

Related

Select from table where ids are not in list of ids

I'm trying to select from a table where the primary_no (VARCHAR(20)) does not include any of the strings in the #IDS variable, but it's not working and I have tried using a CTE, not exists and not in. Neither of them work. The query still selects all of the data even those with the primary_no that are in the #IDS variable.
ALTER PROCEDURE [dbo].[LinkProc]
#IDS VARCHAR(MAX)
/*
DECLARE #IDS VARCHAR(MAX)
SET #IDS = '
''00000447'',
''0000300'',
''2900071'',
''2900192''
'
EXEC LinkProc #IDS = #IDS
*/
AS
WITH cte (id) AS
(
SELECT *
FROM dbo.splitstring(#IDS)
)
SELECT *
FROM link_tb
WHERE grp_id = 4
AND status_cd = 'A'
AND primary_no NOT IN (SELECT id FROM cte)
I have also tried this with no luck:
SELECT *
FROM link_tb lt
WHERE grp_id = 4
AND status_cd = 'A'
AND NOT EXISTS (SELECT id FROM cte c WHERE lt.primary_no = c.id)
The result set from calling (SELECT id FROM cte):
'00000447'
'0000300'
'2900071'
'2900192'
I found a solution to this problem, I answered below.
Disclaimer: Turns out, from the chat, that the OP is not using 2014, but 2005 (which is completely unsupported and has been for years). As a result the answer using a table type parameter will not work because the functionality does not exist.
I have left the answer there, however, for future users who have a similar question.
Instead of using a delimited list, use a table-type parameter
CREATE TYPE dbo.PrimaryList AS TABLE (primary_no varchar(20) NOT NULL);
GO
ALTER PROC dbo.LinkProc #IDs dbo.PrimaryList READONLY AS
BEGIN
SELECT *
FROM dbo.link_tb l
LEFT JOIN #IDs I ON l.primary_no = I.primary_no
WHERE grp_id = 4
AND status_cd = 'A'
AND I.primary_no IS NULL;
END;
GO
You can then call the SP as so:
DECLARE #IDs dbo.PrimaryList;
INSERT INTO #IDs
VALUES('00000447'),
('0000300'),
('2900071'),
('2900192');
EXEC dbo.LinkProc #IDs;
Edit:
As for why what you have isn't working, it's because you're quote wrapping your values. What you're doing is the equivlent of:
EXEC dbo.LinkProc #IDs = '''00000447''';
The value of primary_no isn't going to be '00000447' it's just going to be 00000447. If you have to pass a delimited list (which I suggest against, and I wouldn't be surprised if your function using a WHILE, and if it does you need to remove that), then don't quote the values:
EXEC dbo.LinkProc #IDs = '00000447,0000300,2900071,2900192';
This was the solution to my problem. The IDS were parsed into a table and I selected from there. My main issue was I had set the #IDS with incorrect formatting, there were line breaks in the string. Upon putting them together with no spaces it worked.
ALTER PROCEDURE [dbo].[LinkProc]
#IDS VARCHAR(MAX)
/*
DECLARE #IDS VARCHAR(MAX)
SET #IDS = '00000447,0000300,2900071,2900192'
EXEC LinkProc #IDS = #IDS
*/
AS
SELECT *
FROM link_tb
WHERE grp_id = 4
AND status_cd = 'A'
AND primary_no NOT IN (SELECT param_value FROM dbo.PARSE_PARAM_LIST(#IDS, ','))

Take common between 2 variable list

Considering the following statement:
DECLARE #table_source NVARCHAR(255) = 'ID,PERSONID,ALTROCAMPO,ANCORA,GENDER,LASTNAME,PRIMARYNAME'
DECLARE #table_target NVARCHAR(255) = 'ID,PERSONID,GENDER,LASTNAME,DATES,PRIMARYNAME'
How can I take just the common?
Result should be something like:
#result = 'ID,PERSONID,GENDER,LASTNAME,PRIMARYNAME'
Thank you all
Assuming you're using the latest version of SQL Server, and you must use delimited values, use STRING_SPLIT and STRING_AGG:
DECLARE #table_source nvarchar(255) = N'ID,PERSONID,ALTROCAMPO,ANCORA,GENDER,LASTNAME,PRIMARYNAME';
DECLARE #table_target nvarchar(255) = N'ID,PERSONID,GENDER,LASTNAME,DATES,PRIMARYNAME';
WITH TS AS(
SELECT SS.[value]
FROM STRING_SPLIT(#table_source,',') SS),
TT AS(
SELECT SS.[value]
FROM STRING_SPLIT(#table_target,',') SS)
SELECT STRING_AGG(TS.[value],',') AS Result
FROM TS
JOIN TT ON TS.[value] = TT.[value];
If you aren't on the latest version, you'll need to replace them with a string splitter (i.e. delimitedsplitN4K_LEAD) and use the FOR XML PATH & STUFF method respectively for which ever function(s) you don't have access to.
As I said though, really you should be using a table-type parameter, and then this is trivial. assuming those are actually column names, then you can do:
--Create the type
CREATE TYPE dbo.Object_List AS TABLE (ObjectName sysname);
GO
--Declare variables
DECLARE #table_source dbo.Object_List;
INSERT INTO #table_source (ObjectName)
VALUES (N'ID'),(N'PERSONID'),(N'ALTROCAMPO'),(N'ANCORA'),(N'GENDER'),(N'LASTNAME'),(N'PRIMARYNAME')
DECLARE #table_target dbo.Object_List;
INSERT INTO #table_target (ObjectName)
VALUES (N'ID'),(N'PERSONID'),(N'ALTROCAMPO'),(N'ANCORA'),(N'GENDER'),(N'LASTNAME'),(N'PRIMARYNAME')
--Solution
SELECT ObjectName
FROM #table_source TS
WHERE EXISTS (SELECT 1
FROM #table_target TT
WHERE TS.ObjectName = TT.ObjectName);
Thank you all, i solved with the help of LARNU.
As per last question, i assign the result to a variable.
Here is the code:
WITH TS AS (
SELECT SS.[value]
FROM STRING_SPLIT(#table_source,',') as SS),
TT AS (
SELECT SS.[value]
FROM STRING_SPLIT(#table_target,',') as SS)
SELECT #result = STRING_AGG(TS.[value],',')
FROM TS
JOIN TT ON TS.[value] = TT.[value];

Two variables in where clause

I want to declare multiple variables based on user input and use them all as conditions in a WHERE clause. I have the variables hard set to the values I want right now for testing. I plan on using the #Well and #Analyst variables in a similar manner in the future. Here is the code:
DECLARE #Analysis nvarchar(20)
DECLARE #SQLQuery nvarchar(max)
DECLARE #Formation nvarchar(50)
DECLARE #Well nvarchar(30)
DECLARE #Analyst nvarchar(50)
SET #Analysis = 'Elemental Analysis'
SET #Formation = 'Bruce'
SET #SQLQuery = N'SELECT TB_Projects.JobLog#, TB_Projects.ProjName, COUNT(TB_Samples.Sample#) AS [Total Samples]
FROM TB_Projects INNER JOIN TB_Samples ON TB_Projects.JobLog# = TB_Samples.JobLog#
WHERE TB_Samples.['+ #Analysis +'] = 1 AND TB_Projects.Formation ='+#Formation+' GROUP BY TB_Projects.JobLog#, TB_Projects.ProjName'
EXECUTE(#SQLQuery)
I receive the following error with this code:
Msg 207, Level 16, State 1, Line 3
Invalid column name 'Bruce'.
'Bruce' should be the value returned for the column TB_Projects.Formation, it's not a column name. Why doesn't this work?
You have this in the string:
TB_Projects.Formation = '+#Formation+'
This is turned into:
TB_Projects.Formation = Bruce
See the problem? If you printed out the string before you ran it, the problem would probably be obvious.
The simplest solution is:
TB_Projects.Formation = '''+#Formation+'''
That will add single quotes.
A better solution is to use sp_executesql with a parameter for the value.
you need to wrap Formation value in single quotes:
SET #SQLQuery = N'SELECT TB_Projects.JobLog#, TB_Projects.ProjName, COUNT(TB_Samples.Sample#) AS [Total Samples]
FROM TB_Projects INNER JOIN TB_Samples ON TB_Projects.JobLog# = TB_Samples.JobLog#
WHERE TB_Samples.['+ #Analysis +'] = 1 AND TB_Projects.Formation ='''+#Formation+''' GROUP BY TB_Projects.JobLog#, TB_Projects.ProjName'
EXECUTE(#SQLQuery)

sql procedure gives error "'TBL' is not a recognized built-in function nam"

i have created the below store procedure :
USE [Att]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROCEDURE [dbo].[test]
#DEPTNAME varchar(MAX) = '*'
AS
DECLARE #strSql varchar(max)
Set #strSql = 'SELECT DISTINCT USERINFO.BADGENUMBER, USERINFO.NAME,DEPARTMENTS.DEPTNAME
FROM USERINFO INNER JOIN CHECKINOUT ON USERINFO.USERID = CHECKINOUT.USERID
LEFT JOIN DEPARTMENTS ON DEPARTMENTS.DEPTID = USERINFO.DEFAULTDEPTID
WHERE DEPARTMENTS.DEPTNAME = '+#DEPTNAME+'
group by USERINFO.BADGENUMBER, USERINFO.NAME,DEPARTMENTS.DEPTNAME
'
EXEC (#strSql)
It is created successfully but when i execute this procedure then it gives error like
Msg 195, Level 15, State 10, Line 3
'TBL' is not a recognized built-in function name.
but when i execute this procedure as a SQL query then it is working fine
It gives error only through procedure.
Why do you write a stored procedure just to execute a (wrongly, as you're missing the single quotes in the WHERE clause) concatenated SQL string? Try this:
USE [Att]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[test]
#DEPTNAME varchar(MAX) = '*'
AS BEGIN
SELECT DISTINCT
USERINFO.BADGENUMBER,
USERINFO.NAME,
DEPARTMENTS.DEPTNAME
FROM USERINFO
INNER JOIN CHECKINOUT ON USERINFO.USERID = CHECKINOUT.USERID
LEFT JOIN DEPARTMENTS ON DEPARTMENTS.DEPTID = USERINFO.DEFAULTDEPTID
WHERE DEPARTMENTS.DEPTNAME = #DEPTNAME
GROUP BY
USERINFO.BADGENUMBER, USERINFO.NAME, DEPARTMENTS.DEPTNAME
END
Such problems almost always occur when people, instead of using proper SQL try to do stuff using concatenated SQL. This almost always leads to problems.
Its the issue with the quotation mark for #DEPTNAME
try this
Set #strSql = 'SELECT DISTINCT USERINFO.BADGENUMBER, USERINFO.NAME,DEPARTMENTS.DEPTNAME
FROM USERINFO INNER JOIN CHECKINOUT ON USERINFO.USERID = CHECKINOUT.USERID
LEFT JOIN DEPARTMENTS ON DEPARTMENTS.DEPTID = USERINFO.DEFAULTDEPTID
WHERE DEPARTMENTS.DEPTNAME = '''+#DEPTNAME+'''
group by USERINFO.BADGENUMBER, USERINFO.NAME,DEPARTMENTS.DEPTNAME'
You seem to have missed quotes before appending your parameter. Try it like
Set #strSql = 'SELECT DISTINCT USERINFO.BADGENUMBER, USERINFO.NAME,DEPARTMENTS.DEPTNAME
FROM USERINFO INNER JOIN CHECKINOUT ON USERINFO.USERID = CHECKINOUT.USERID
LEFT JOIN DEPARTMENTS ON DEPARTMENTS.DEPTID = USERINFO.DEFAULTDEPTID
WHERE DEPARTMENTS.DEPTNAME = '''+#DEPTNAME+'''
group by USERINFO.BADGENUMBER, USERINFO.NAME,DEPARTMENTS.DEPTNAME
'
Although I agree with #Thorsten that unless its absolutely necessary, do not query using concatenated sql.

SQL variable to hold list of integers

I'm trying to debug someone else's SQL reports and have placed the underlying reports query into a query windows of SQL 2012.
One of the parameters the report asks for is a list of integers. This is achieved on the report through a multi-select drop down box. The report's underlying query uses this integer list in the where clause e.g.
select *
from TabA
where TabA.ID in (#listOfIDs)
I don't want to modify the query I'm debugging but I can't figure out how to create a variable on the SQL Server that can hold this type of data to test it.
e.g.
declare #listOfIDs int
set listOfIDs = 1,2,3,4
There is no datatype that can hold a list of integers, so how can I run the report query on my SQL Server with the same values as the report?
Table variable
declare #listOfIDs table (id int);
insert #listOfIDs(id) values(1),(2),(3);
select *
from TabA
where TabA.ID in (select id from #listOfIDs)
or
declare #listOfIDs varchar(1000);
SET #listOfIDs = ',1,2,3,'; --in this solution need put coma on begin and end
select *
from TabA
where charindex(',' + CAST(TabA.ID as nvarchar(20)) + ',', #listOfIDs) > 0
Assuming the variable is something akin to:
CREATE TYPE [dbo].[IntList] AS TABLE(
[Value] [int] NOT NULL
)
And the Stored Procedure is using it in this form:
ALTER Procedure [dbo].[GetFooByIds]
#Ids [IntList] ReadOnly
As
You can create the IntList and call the procedure like so:
Declare #IDs IntList;
Insert Into #IDs Select Id From dbo.{TableThatHasIds}
Where Id In (111, 222, 333, 444)
Exec [dbo].[GetFooByIds] #IDs
Or if you are providing the IntList yourself
DECLARE #listOfIDs dbo.IntList
INSERT INTO #listofIDs VALUES (1),(35),(118);
You are right, there is no datatype in SQL-Server which can hold a list of integers. But what you can do is store a list of integers as a string.
DECLARE #listOfIDs varchar(8000);
SET #listOfIDs = '1,2,3,4';
You can then split the string into separate integer values and put them into a table. Your procedure might already do this.
You can also use a dynamic query to achieve the same outcome:
DECLARE #SQL nvarchar(8000);
SET #SQL = 'SELECT * FROM TabA WHERE TabA.ID IN (' + #listOfIDs + ')';
EXECUTE (#SQL);
Note: I haven't done any sanitation on this query, please be aware that it's vulnerable to SQL injection. Clean as required.
For SQL Server 2016+ and Azure SQL Database, the STRING_SPLIT function was added that would be a perfect solution for this problem. Here is the documentation:
https://learn.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql
Here is an example:
/*List of ids in a comma delimited string
Note: the ') WAITFOR DELAY ''00:00:02''' is a way to verify that your script
doesn't allow for SQL injection*/
DECLARE #listOfIds VARCHAR(MAX) = '1,3,a,10.1,) WAITFOR DELAY ''00:00:02''';
--Make sure the temp table was dropped before trying to create it
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable;
--Create example reference table
CREATE TABLE #MyTable
([Id] INT NOT NULL);
--Populate the reference table
DECLARE #i INT = 1;
WHILE(#i <= 10)
BEGIN
INSERT INTO #MyTable
SELECT #i;
SET #i = #i + 1;
END
/*Find all the values
Note: I silently ignore the values that are not integers*/
SELECT t.[Id]
FROM #MyTable as t
INNER JOIN
(SELECT value as [Id]
FROM STRING_SPLIT(#listOfIds, ',')
WHERE ISNUMERIC(value) = 1 /*Make sure it is numeric*/
AND ROUND(value,0) = value /*Make sure it is an integer*/) as ids
ON t.[Id] = ids.[Id];
--Clean-up
DROP TABLE #MyTable;
The result of the query is 1,3
In the end i came to the conclusion that without modifying how the query works i could not store the values in variables. I used SQL profiler to catch the values and then hard coded them into the query to see how it worked. There were 18 of these integer arrays and some had over 30 elements in them.
I think that there is a need for MS/SQL to introduce some aditional datatypes into the language. Arrays are quite common and i don't see why you couldn't use them in a stored proc.
There is a new function in SQL called string_split if you are using list of string.
Ref Link STRING_SPLIT (Transact-SQL)
DECLARE #tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(#tags, ',')
WHERE RTRIM(value) <> '';
you can pass this query with in as follows:
SELECT *
FROM [dbo].[yourTable]
WHERE (strval IN (SELECT value FROM STRING_SPLIT(#tags, ',') WHERE RTRIM(value) <> ''))
I use this :
1-Declare a temp table variable in the script your building:
DECLARE #ShiftPeriodList TABLE(id INT NOT NULL);
2-Allocate to temp table:
IF (SOME CONDITION)
BEGIN
INSERT INTO #ShiftPeriodList SELECT ShiftId FROM [hr].[tbl_WorkShift]
END
IF (SOME CONDITION2)
BEGIN
INSERT INTO #ShiftPeriodList
SELECT ws.ShiftId
FROM [hr].[tbl_WorkShift] ws
WHERE ws.WorkShift = 'Weekend(VSD)' OR ws.WorkShift = 'Weekend(SDL)'
END
3-Reference the table when you need it in a WHERE statement :
INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM #ShiftPeriodList)
You can't do it like this, but you can execute the entire query storing it in a variable.
For example:
DECLARE #listOfIDs NVARCHAR(MAX) =
'1,2,3'
DECLARE #query NVARCHAR(MAX) =
'Select *
From TabA
Where TabA.ID in (' + #listOfIDs + ')'
Exec (#query)

Resources