SQL Server : query that doesn't work with parameters - sql-server

I am not sure why this query works one way and not the other.
If I use the following query:
Select
dlb.referenceid, dlb.partnum,
pd.width, pd.length,
dlb.partqty,
Convert(date, dlb.entrydatetime, 101) as entrydatetime,
dlb.status, dlb.material
From
tbl_dlbase dlb
Join
tbl_partdetails pd On dlb.referenceid = pd.referenceid
Where
(pd.Spacing = 3)
And ((dlb.status = 'Available') or (dlb.status = 'Reserved'))
Order By
dlb.status
It returns one result as it should.
If I use the exact same query but with parameters:
Select
dlb.referenceid, dlb.partnum,
pd.width, pd.length,
dlb.partqty,
Convert(date, dlb.entrydatetime, 101) as entrydatetime,
dlb.status, dlb.material
From
tbl_dlbase dlb
Join
tbl_partdetails pd On dlb.referenceid = pd.referenceid
Where
(#parameter1 = #Criteria1)
And ((dlb.status = 'Available') or (dlb.status = 'Reserved'))
Order By
dlb.status
And have the following parameters declared and set as follows:
Declare #parameter1 varchar(50)
Declare #Criteria1 varchar(50)
Set #parameter1 = 'pd.Spacing'
Set #Criteria1 = 3
The query returns no results.
I have used this type parameter query many times before but for some reason this time it won't work. Can anyone tell me what stupid thing I am missing or screwed up? I can't seem to see anything wrongs with it.

You can't use parameters as object names without dynamic sql. Here is a fix... just change the print to exec(#sql) when you are happy with it...
Declare #parameter1 varchar(50)
Declare #Criteria1 varchar(50)
Set #parameter1 = 'pd.Spacing'
Set #Criteria1 = '3'
declare #sql varchar(max)
set #sql =
'Select
dlb.referenceid,
dlb.partnum,
pd.width,
pd.length,
dlb.partqty,
Convert(date, dlb.entrydatetime, 101) as entrydatetime,
dlb.status,
dlb.material
From tbl_dlbase dlb
Join tbl_partdetails pd On
dlb.referenceid = pd.referenceid
Where (' + #parameter1 + ' = ' + #Criteria1 + ')
AND ((dlb.status = ''Available'') OR (dlb.status = ''Reserved''))
Order By dlb.status'
print #sql
--exec(#sql)
--sp_executesql #sql
This makes your where clause Where (pd.Spacing = 3) otherwise the where clause is simply comparing the parameters, which obviously aren't the same since they aren't set to the same thing. It's the same thing as writing:
Select 'True'
where #parameter1 = #Criteria1
which is interpreted as
Select 'True'
where 'pd.Spacing'= '3' --string literals, not column names.

Related

T-SQL Where clause with nvarchar composed by a string_agg in openquery

I'm creating a stored procedure where I have this openquery:
SELECT #MY_QUERY = 'SELECT * FROM OPENQUERY(HYPER_CONN, ''
SELECT COALESCE(SUM(QUANT_RIGA),0) FROM DDT_CLI_RIGHE WHERE DOC_RIGA_ID = ''''' + #THE_DDT + ''''' '')'
In the where clause I set this variable #THEDDT:
DECLARE #THE_DDT nvarchar(MAX) = (SELECT STRING_AGG(DOC_RIGA_ID,',') FROM ...
For example it will be like #THEDDT = test1,test2,test3
I want set this variable in my "where in" clause.
So in the openquery I'm trying to have something like this (but using the varaible):
WHERE DOC_RIGA_ID IN ('test1','test2','test3')
Is there a way to do this?
This is more of a stab in the dark, as we don't have all the information, however, as I mentioned, it seems all you need to do is change your DOC_RIGA_ID = {literal} clause to a DOC_RIGA_ID IN ({delimited List}) clause:
DECLARE #THE_DDT nvarchar(MAX) = (SELECT STRING_AGG(QUOTENAME(DOC_RIGA_ID,''''),',') --Assumes DOC_RIGA_ID can't have a value longer than 128 characters
FROM ...
SELECT #MY_QUERY = 'SELECT * FROM OPENQUERY(HYPER_CONN, ''
SELECT COALESCE(SUM(QUANT_RIGA),0) FROM DDT_CLI_RIGHE WHERE DOC_RIGA_ID IN (' + REPLACE(#THE_DDT,'''','''''') + ')'') OQ;';
This is, of course, untested as I have no way of testing this statement.

In T-SQL is there a way to say that if this parameter is equal to "XYZ" then use "XYZ" but if not return all?

I'm currently creating stored procedures on the SQL server by using linked servers, "OPENQUERY" statements, and temporary tables. My goal is to have one source that will be consumed by multiple third party sources so that everyone is viewing the same data.
Where I'm running into my problem is that some instances need a specific where clause where others don't need this where clause. Is there a way to Declare this where clause equal to something that nullifies that where clause if it's blank but use the where clause if it's populated? I've tried making the parameter equal to "%", "%?%", etc. but nothing seems to work.
I would also like to point out that this is an Oracle Database that I'm pulling from on a Microsoft SQL Server. My code is below and the parameter #WINS is what I'm trying to nullify if left blank:
DECLARE #query_start DATETIME;
DECLARE #query_end DATETIME;
DECLARE #query_wins NVARCHAR(MAX);
SET #query_start = '7/1/2020';
SET #query_end = '7/15/2020';
SET #query_wins = 'F6666';
DECLARE #START_DATE NVARCHAR(MAX) = CONVERT(VARCHAR,#query_start,105)
DECLARE #END_DATE NVARCHAR(MAX) = CONVERT(VARCHAR,#query_end,105)
DECLARE #WINS NVARCHAR(MAX) = #query_wins
DECLARE #SqlCommand NVARCHAR(MAX) =
'
SELECT
*
FROM
OPENQUERY
(
PDB,
'' SELECT
T1.WELL_NUM
, D2.WELL_NAME
, T1.DAILY_RDG_DATE
, T1.GROSS_OIL_BBLS
, T1.GROSS_GAS_MCF
, T1.GROSS_WTR_BBLS
, T1.TUBING_PRESS
, T1.CASING_PRESS
, T1.GAS_LINE_PRESS
, T1.CHOKE,T1.CHOKE_SIZE AS CHOKE2
, T2.GAS_PROD_FORECAST
, T2.OIL_PROD_FORECAST
, T2.WTR_PROD_FORECAST
FROM
(PDB.T003031 T1
INNER JOIN WINS.DW_ANORM_ROWL#WINP_DBLINK.WORLD D2
ON T1.WELL_NUM = D2.WINS_NO
AND T1.CMPL_NUM = D2.CMPL_NO)
LEFT JOIN PDB.T000057 T2 ON T1.WELL_NUM = T2.WELL_NUM
AND T1.CMPL_NUM = T2.CMPL_NUM
AND T2.FORECAST_DATE=T1.DAILY_RDG_DATE
WHERE
D2.HOLE_DIRECTION = ''''HORIZONTAL''''
AND D2.ASSET_GROUP = ''''Powder River Basin''''
AND T1.DAILY_RDG_DATE > TO_DATE(''''' + CONVERT(VARCHAR,#START_DATE,105) + ''''',''''DD-MM-YYYY'''') - 2
AND T1.DAILY_RDG_DATE < TO_DATE(''''' + CONVERT(VARCHAR,#END_DATE,105) + ''''',''''DD-MM-YYYY'''')
AND D2.OPER_NON_OPER = ''''OPERATED''''
AND T1.WELL_NUM = ''''' + #WINS + '''''
''
)
'
PRINT #SqlCommand
DROP TABLE IF EXISTS #temp
CREATE TABLE #temp (
WELL_NUM NVARCHAR(MAX)
, WELL_NAME NVARCHAR(MAX)
, DAILY_RDG_DATE DATETIME
, GROSS_OIL_BBLS FLOAT
, GROSS_GAS_MCF FLOAT
, GROSS_WTR_BBLS FLOAT
, TUBING_PRESS FLOAT
, CASING_PRESS FLOAT
, GAS_LINE_PRESS FLOAT
, CHOKE1 FLOAT
, CHOKE2 FLOAT
, GAS_PROD_FORECAST FLOAT
, OIL_PROD_FORECAST FLOAT
, WTR_PROD_FORECAST FLOAT
)
PRINT #SqlCommand
INSERT INTO #temp
EXEC sp_ExecuteSQL #SqlCommand
SELECT
WELL_NUM
, WELL_NAME
, DAILY_RDG_DATE
, ISNULL(GROSS_OIL_BBLS,0) AS 'GROSS_OIL_BBLS'
, ISNULL(GROSS_GAS_MCF,0) AS 'GROSS_GAS_MCF'
, ISNULL(GROSS_WTR_BBLS,0) AS 'GROSS_WTR_BBLS'
, ISNULL(TUBING_PRESS,0) AS 'TUBING_PRESS'
, ISNULL(CASING_PRESS,0) AS 'CASING_PRESS'
, ISNULL(GAS_LINE_PRESS,0) AS 'GAS_LINE_PRESS'
, ISNULL(CHOKE1,0) AS 'CHOKE1'
, ISNULL(CHOKE2,0) AS 'CHOKE2'
, ISNULL(GAS_PROD_FORECAST,0) AS 'CHOKE2'
, ISNULL(OIL_PROD_FORECAST,0) AS 'OIL_PROD_FORECAST'
, ISNULL(WTR_PROD_FORECAST,0) AS 'WTR_PROD_FORECAST'
FROM #temp
ORDER BY
DAILY_RDG_DATE ASC
DROP TABLE IF EXISTS #temp
You can set up an optional parameter like this in SQL Server
WHERE ISNULL(#parameter,column_name) = column_name
Ok, if I abstract your problem properly, you're trying to apply different filtering logic based on the value of a field.
You might consider using a cursor for this, iterate over the table and apply if / else as needed.
Do you really need to compose the SQL command as a string? Maybe you're only doing that to share the code here, hopefully?
Anyway, It sounds like this would work for you:
WHERE (T1.WELL_NUM = #WINS OR #WINS IS NULL) AND
...other conditions...

Change a table name in SQL Server procedure

I want this procedure change the table name when I execute it.
The table name that I want to change is Recargas_#mes
There is some way to do that?
#MES DATETIME
AS
BEGIN
SELECT CUENTA, SUM(COSTO_REC) COSTO_REC
INTO E09040_DEV.BI_PRO_COSTO_RECARGAS
FROM (
SELECT a.*,(CASE
WHEN COD_AJUSTE IN ('ELEC_TEXT','TFREPPVV_C') THEN (A.VALOR)*(R.COSTO) ELSE 0 END)
FROM Recargas_#MES AS A, BI_PRO_LISTA_COSTOS_RECARGAS AS R
WHERE R.ANO_MES = #MES
) D
GROUP BY CUENTA
END
Sample code:
-- Declare variables
DECLARE #MES DATETIME;
DECLARE #TSQL NVARCHAR(MAX);
-- Set the variable to valid statement
SET #TSQL = N'
SELECT CUENTA, SUM(COSTO_REC) AS COSTO_REC
INTO E09040_DEV.BI_PRO_COSTO_RECARGAS
FROM (
SELECT A.*,
(CASE
WHEN COD_AJUSTE IN (''ELEC_TEXT'',''TFREPPVV_C'') THEN
(A.VALOR)*(R.COSTO)
ELSE 0
END)
FROM
Recargas_' + REPLACE(CONVERT(CHAR(10), #MES, 101), '/', '') + ' AS A,
BI_PRO_LISTA_COSTOS_RECARGAS AS R
WHERE R.ANO_MES = ' + CONVERT(CHAR(10), #MES, 101) + '
) D
GROUP BY CUENTA'
-- Execute the statement
EXECUTE (#SQL)
Some things to note:
1 - I assume the table name has some type of extension that is a date? I used MM/DD/YYYY and removed the slashes as a format for the suffix.
2 - The WHERE clause will only work if you are not using the time part of the variable.
For instance, 03/15/2016 00:00:00 would be date without time entry. If not, you will have to use >= and < to grab all hours for a particular day.
3 - You are creating a table on the fly with this code. On the second execution, you will get a error unless you drop the table.
4 - You are not using the ON clause when joining table A to table R. To be ANSI compliant, move the WHERE clause to a ON clause.
5 - The actual calculation created by the CASE statement is not give a column name.
Issues 3 to 5 have to be solved on your end since I do not have the detailed business requirements.
Have Fun.
It should work using dynamic SQL to allow putting a dynamic table name:
DECLARE #SQL NVARCHAR(MAX) = N'
SELECT CUENTA, SUM(COSTO_REC) COSTO_REC
INTO E09040_DEV.BI_PRO_COSTO_RECARGAS
FROM (
SELECT a.*,(CASE
WHEN COD_AJUSTE IN (''ELEC_TEXT'',''TFREPPVV_C'') THEN (A.VALOR)*(R.COSTO) ELSE 0 END)
FROM Recargas_' + #MES + ' AS A, BI_PRO_LISTA_COSTOS_RECARGAS AS R
WHERE R.ANO_MES = ' + CAST(#MES AS VARCHAR(32)) + '
) D
GROUP BY CUENTA'
EXECUTE (#SQL)

TSQL - Error in stored procedure due to conversion failure

I have this situation in a stored Procedure:
SET #DATE_RELEASE_START = '2015-01-01';
SET #DATE_RELEASE_END = '2015-05-31'
SELECT #statement = ' SELECT *
FROM (SELECT AFCDENTE, M.ID_MODIFICATION_CODE, COUNT(*) AS Conteggio--, CAST((COUNT(*) * 100/ 15032) AS decimal(10,7)) AS Percentage
FROM CIC_LOG_MODIFICHE AS L INNER JOIN ADM_MODIFICATION_CODE AS M ON L.CD_MODIFICATION_CODE = M.CD_MODIFICATION_CODE
INNER JOIN CIC_PRODUZIONE AS P ON P.CD_CIC_PRODUZIONE = L.CD_CIC_PRODUZIONE
WHERE AFDTMODI BETWEEN '+#DATE_RELEASE_START+' AND '+#DATE_RELEASE_END+' AND P.CD_PLANT = '+#CD_PLANT+' AND AFCDENTE IS NOT NULL
GROUP BY AFCDENTE, M.ID_MODIFICATION_CODE) as tbl
PIVOT (SUM(tbl.Conteggio) for tbl.ID_MODIFICATION_CODE in (' + #columns + ')) as pvt'
I get this error:
Conversion failed when converting date and/or time from character
string.
I tried casting those dates but no changes. I get the same error.
What should I do?
I'd suggest doing it like that:
SET #DATE_RELEASE_START = '2015-01-01';
SET #DATE_RELEASE_END = '2015-05-31'
SELECT #statement = ' SELECT *
FROM (SELECT AFCDENTE, M.ID_MODIFICATION_CODE, COUNT(*) AS Conteggio--, CAST((COUNT(*) * 100/ 15032) AS decimal(10,7)) AS Percentage
FROM CIC_LOG_MODIFICHE AS L INNER JOIN ADM_MODIFICATION_CODE AS M ON L.CD_MODIFICATION_CODE = M.CD_MODIFICATION_CODE
INNER JOIN CIC_PRODUZIONE AS P ON P.CD_CIC_PRODUZIONE = L.CD_CIC_PRODUZIONE
WHERE AFDTMODI BETWEEN #p0 AND #p1 AND P.CD_PLANT = #p2 AND AFCDENTE IS NOT NULL
GROUP BY AFCDENTE, M.ID_MODIFICATION_CODE) as tbl
PIVOT (SUM(tbl.Conteggio) for tbl.ID_MODIFICATION_CODE in (' + #columns + ')) as pvt'
EXECUTE sp_executesql #statement, N'#p0 DATETIME2, #p1 DATETIME2, #p0 NVARCHAR(1000)', #p0 = #DATE_RELEASE_START, #p1 = #DATE_RELEASE_END, #p2 = #CD_PLANT;
Instead casting them as VARCHAR, just pass them as variables and then use sp_executesql to do the right job.
You must quote the date strings in your SQL code properly:
WHERE AFDTMODI BETWEEN '''+#DATE_RELEASE_START+''' AND '''+#DATE_RELEASE_END+'''
Otherwise the SQL will read
WHERE AFDTMODI BETWEEN 2015-01-01 AND 2015-05-31
instead of
WHERE AFDTMODI BETWEEN '2015-01-01' AND '2015-05-31'
We've also had problems before with passing date/time values to stored procedures in Management Studio. We needed to give the date as 20150101 or 20150531.

SQL Server 2005 - column names based on variable passed in pivot query

I have following query which takes 2 parameters.
YearNumber
MonthNumber
In my pivot query, I am trying to select columns based on #Year_Rtl variable. I need to select data for the year passed, last year and last last year. Since the data being displayed on UI is table format divided by #Year_Rtl, I decided to write a pivot query for that as below.
In the query, it works fine if I hard code [#Year_Rtl], [#Year_Rtl - 1], [#Year_Rtl - 2] to [2012], [2011], [2010]. But since the year passed can be anything, I want columns to be named dynamically.
DECLARE #Month_Rtl int
DECLARE #Year_Rtl int
SET #Year_Rtl = 2012
SET #Month_Rtl = 1
SELECT
'Data 1', [#Year_Rtl], [#Year_Rtl - 1], [#Year_Rtl - 2]
FROM
(SELECT [Yr_No], Qty
FROM dbo.Table1 t
WHERE (t.Col1 = 10) AND
(t.Col2 = '673') AND
((t.Mth_No = #Month_Rtl AND t.Yr_No = #Year_Rtl) OR
(t.Mth_No = 12 AND t.Yr_No IN (#Year_Rtl - 1, #Year_Rtl - 2)))
) p PIVOT (SUM(Qty)
FOR [Yr_No] IN ([#Year_Rtl], [#Year_Rtl-1], [#Year_Rtl-2])
) AS pvt
Above query throws following errors:
Error converting data type nvarchar to smallint.
The incorrect value "#Year_Rtl" is supplied in the PIVOT operator.
Invalid column name '#Year_Rtl - 1'.
Invalid column name '#Year_Rtl - 2'.
Since you can use dynamic SQL, I'd go with a macro-replacement approach. You're identifying areas of the query that must be dynamically replaced with placeholders (e.g. $$Year_Rtl) and then calculating their replacement values below. I find that it keeps the SQL statement easy to follow.
DECLARE #SQL NVarChar(2000);
SELECT #SQL = N'
SELECT
''Data 1'', [$$Year_Rtl], [$$Year_RtlM1], [$$Year_RtlM2]
FROM
(SELECT [Yr_No], Qty
FROM dbo.Table1 t
WHERE (t.Col1 = 10) AND
(t.Col2 = ''673'') AND
((t.Mth_No = $$Month_Rtl AND t.Yr_No = $$Year_Rtl) OR
(t.Mth_No = 12 AND t.Yr_No IN ($$Year_RtlM1, $$Year_RtlM2)))
) p PIVOT (SUM(Qty)
FOR [Yr_No] IN ([$$Year_Rtl], [$$Year_RtlM1], [$$Year_RtlM2])
) AS pvt';
SELECT #SQL = REPLACE(#SQL, '$$Year_RtlM2', #Year_Rtl - 2);
SELECT #SQL = REPLACE(#SQL, '$$Year_RtlM1', #Year_Rtl - 1);
SELECT #SQL = REPLACE(#SQL, '$$Year_Rtl', #Year_Rtl);
SELECT #SQL = REPLACE(#SQL, '$$Month_Rtl', #Month_Rtl);
PRINT #SQL;
-- Uncomment the next line to allow the built query to execute...
--EXECUTE sp_ExecuteSQL #SQL;
Since consuming code will also have to be flaky under this scheme (e.g. selecting columns based on "position" rather than name) - why not normalize the columns by performing a DATEDIFF(year,Yr_No,#Year_Rtl), and work from there? Those columns will always be 0, -1 and -2...
You need to look into Dynamic SQL Pivoting.
I recommend reading Itzik Ben-Gan's T-SQL Fundamentals where he goes over how to do this.
Alternatively try this article if you don't want to buy the book.
Maybe this will help:
First getting the columns with a tally function like this:
DECLARE #Month_Rtl int,
#Year_Rtl int,
#Year_Rtl_Start INT,
#cols VARCHAR(MAX),
#values VARCHAR(MAX)
SET #Year_Rtl = 2012
SET #Month_Rtl = 1
SET #Year_Rtl_Start=2009
;WITH Years ( n ) AS (
SELECT #Year_Rtl_Start UNION ALL
SELECT 1 + n FROM Years WHERE n < #Year_Rtl )
SELECT
#cols = COALESCE(#cols + ','+QUOTENAME(n),
QUOTENAME(n)),
#values = COALESCE(#values + ','+CAST(n AS VARCHAR(100)),
CAST(n AS VARCHAR(100)))
FROM
Years
ORDER BY n DESC
The variable #cols contains the columns that is in the pivot and the variable #values contains the years for the IN. The #Year_Rtl is the end year and the #Year_Rtl_Start is the start for you range.
Then declaring and executing the dynamic pivot like this:
DECLARE #query NVARCHAR(4000)=
N'SELECT
''Data 1'', '+#cols+'
FROM
(
SELECT
[Yr_No], Qty
FROM
dbo.Table1 t
WHERE
t.Col1 = 10
AND t.Col2 = ''673''
AND
(
(
t.Mth_No = '+CAST(#Month_Rtl AS VARCHAR(10))+'
AND t.Yr_No = '+CAST(#Year_Rtl AS VARCHAR(10))+'
)
OR
(
t.Mth_No = 12
AND t.Yr_No IN ('+#values+'))
)
) p
PIVOT
(
SUM(Qty)
FOR [Yr_No] IN ('+#cols+')
) AS pvt'
EXECUTE(#query)

Resources