Throwing conversion error while pass the value directly - sql

This code throws an error when pass the value directly, but it doesn't show any error if pass the value by using parameter.
--It throws an Error
DECLARE #sql NVARCHAR(4000)
DECLARE #ID INT=1234
SET #sql = N'select
[count]
FROM dbo.Table_1 AS t
JOIN dbo.table_2 AS t2 ON t.store_number = t2.store_number
AND t2.[year] = 17
AND t2.week_of_year = 6
AND t2.day_of_week = 2
WHERE t.RC_ID = #ID'
EXEC sp_executesql #sql
-- It throws an error
select
[count]
FROM dbo.Table_1 AS t
JOIN dbo.table_2 AS t2 ON t.store_number = t2.store_number
AND t2.[year] = 17
AND t2.week_of_year = 6
AND t2.day_of_week = 2
WHERE t.ID = 1234
-- IT WORKS
DECLARE #sql
DECLARE #ID INT
SET #ID = 1234
select
[count]
FROM dbo.Table_1 AS t
JOIN dbo.table_2 AS t2 ON t.store_number = t2.store_number
AND t2.[year] = 17
AND t2.week_of_year = 6
AND t2.day_of_week = 2
WHERE t.ID = #ID
The Error is :
Msg 245, Level 16, State 1, Line 1 Conversion failed when converting
the varchar value 'TEST' to data type int.
But there is No data like 'Test' in the table.

One of your values that you are comparing as integers contains a bad value:
select t2.*
from table_2 t2
where try_convert(int, year) is null or try_convert(int, week_of_year) is null or
try_convert(int, day_of_week) or try_convert(int, id) is null;
Whether the error occurs depends on the execution plan.

from what i see you are trying to use a parameter(#id) into the sp_executeSQL without never passing it. A quick fix would be to do something like that
DECLARE #sql NVARCHAR(4000)
DECLARE #ID INT = 10
SET #sql = N'select
[count]
FROM dbo.Table_1 AS t
JOIN dbo.table_2 AS t2 ON t.store_number = t2.store_number
AND t2.[year] = 17
AND t2.week_of_year = 6
AND t2.day_of_week = 2
WHERE t.RC_ID = ' + cast(#ID as nvarchar(20))
EXEC sp_executesql #sql
Hope this helps

One of your table columns that you are filtering is not a numeric data type. When you do
WHERE VarcharColumn = 1
The SQL Server engine will always try to convert the most complex type to the simplest one, in this case "1" is a integer and VarcharColumn is VARCHAR, so the engine will try to convert all the values stored in VarcharColumn to integer before filtering by value 1. Since at least one value stored there is not an integer ("TEST") then the conversion fails and that message pops up.
You have 2 solutions:
Validate and fix all your values in those columns so they are actually numbers and alter the data type to the corresponding one.
Compare against the same type. WHERE Column = '1'
Of course always try to keep your data types in check.
Also in your dynamicSQL query, the declaration of the #ID must be inside your script (it's also missing an initial value).
DECLARE #sql NVARCHAR(4000)
SET #sql = N'
DECLARE #ID INT = 1
select
[count]
FROM dbo.Table_1 AS t
JOIN dbo.table_2 AS t2 ON t.store_number = t2.store_number
AND t2.[year] = 17
AND t2.week_of_year = 6
AND t2.day_of_week = 2
WHERE t.RC_ID = #ID'
EXEC sp_executesql #sql
The reason for the error poping up 'sometimes' is because the different forms of your statement are making the execution plan do things in different order. If it tries to convert the varchar value to int first, it will fail. If it tries to convert the int value to varchar (for example) then it won't fail.
To find the problem, try this:
SELECT
*
FROM
dbo.Table_1 AS T
WHERE
CONVERT(VARCHAR(200), T.store_number) = 'Test' OR
CONVERT(VARCHAR(200), T.ID) = 'Test'
SELECT
*
FROM
dbo.table_2 AS T
WHERE
CONVERT(VARCHAR(200), T.store_number) = 'Test' OR
CONVERT(VARCHAR(200), T.[year]) = 'Test' OR
CONVERT(VARCHAR(200), T.week_of_year) = 'Test' OR
CONVERT(VARCHAR(200), T.day_of_week) = 'Test'

Related

SET more than 1 row output into 1 variable

I am wanting to be able to save more than just 1 output from a SELECT query in a single variable.
Currently I am gathering my needed data like so:
DECLARE #something1 VARCHAR(MAX)
DECLARE #something2 VARCHAR(MAX)
DECLARE #something3 VARCHAR(MAX)
SET #something1 = (
SELECT
Custom AS 'XXL Format'
FROM
tblData
WHERE
ID = 1);
SET #something2 = (
SELECT
Custom.value('(/Individual/text())[1]', 'varchar(MAX)') AS 'Non XML Format'
FROM
tblData
WHERE
ID = 1)
SET #something3 = (
SELECT
tbl1.paper,
tbl2.type
FROM
tblData AS tbl1
JOIN tblData2 tbl2
ON tbl1.ID = tbl2.ID
WHERE
ID = 1);
I have the following demo that shows what I am wanting to do
DECLARE #tester VARCHAR(MAX)
SET #tester = (
SELECT
tbl1.Custom AS 'XXL Format',
tbl1.Custom.value('(/Individual/text())[1]', 'varchar(MAX)') AS 'Non XML Format'
tbl1.paper,
tbl2.type
FROM
tblData AS tbl1
JOIN tblData2 tbl2
ON tbl1.ID = tbl2.ID
WHERE
ID = 1);
I get the error of:
Only one expression can be specified in the select list when
the subquery is not introduced with EXISTS
Both demos can be found here
I have tried to set the variable to "table" and store the data like that but that also does not seem to work correctly [I'm sure I am doing something wrong - that may be the answer to this question I'm asking]
How can I just use 1 variable for all that the above query outputs?
Your error is that the subquery returned more than one value. You can use top if you don't care which value gets assigned:
SET #tester = (SELECT TOP (1) description FROM ForgeRock);
You have to return only one value you can try top or limit like SET #tester = (SELECT TOP 1 description FROM ForgeRock); or SET #tester = (SELECT description FROM ForgeRock LIMIT 1);
So I guess I was correct with the set the variable to a temp "table". I finally just now got it to work for my needs!
DECLARE #tmpTbl table (_xml, _parsedXML, _paper, _type)
INSERT INTO #tmpTbl
SELECT
tbl1.Custom AS 'XML Format',
tbl1.Custom.value('(/Individual/text())[1]', 'varchar(MAX)') AS 'Non XML Format',
tbl1.paper,
tbl2.type
FROM
tblData AS tbl1
JOIN tblData2 AS tbl2
ON tbl1.ID = tbl2.ID
WHERE
ID = 1;
DECLARE #something1 VARCHAR(MAX) = (SELECT _xml FROM #tmpTbl);
DECLARE #something2 VARCHAR(MAX) = (SELECT _parsedXML FROM #tmpTbl);
DECLARE #something3 VARCHAR(MAX) = (SELECT _paper FROM #tmpTbl);
DECLARE #something4 VARCHAR(MAX) = (SELECT _type FROM #tmpTbl);
DELETE FROM #tmpTbl --Not really needed but nice to be offical :)
This above stores the values into one place. Although its not inside 1 variable I guess having to create a temp table isn't all that bad for the database/performance...

MSSQL says I am trying to convert a varchar to a string when I'm not

So I have this fairly long procedure at Work that I just made. What it does it not that important, but the end result is what matters.
I need to count some different types of descriptions in a table and that Works fine. I then need to take the two things that I Count and put them in a string that I return to my software. However, every time I run this procedure it gives me this:
Msg 245, Level 16, State 1, Procedure WorkDays, Line 43 Conversion
failed when converting the varchar value
'FlightDeck:161,CabinCrew:189' to data type int.
I just can't figure out why it keeps telling me this when I am not trying to convert a varchar to an int but rather ints to a single varchar.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[WorkDays] #requestedDate nchar(10)
AS
SET ANSI_WARNINGS OFF
DECLARE #date as nchar(10) = ''
DECLARE #returnVal as varchar(30) = ''
DECLARE #flightDeck as int = 0
DECLARE #cabinCrew as int = 0
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #date = #requestedDate
SELECT
#flightDeck = SUM(CASE WHEN dbo.Crew_Category.Description LIKE 'Flight Deck' THEN 1 END),
#cabinCrew = SUM(CASE WHEN dbo.Crew_Category.Description LIKE 'Cabin Crew' THEN 1 END)
FROM
dbo.CrewMember INNER JOIN
dbo.Crew_Category ON dbo.CrewMember.CrewCategorySeqNo = dbo.Crew_Category.CrewCategorySeqno
WHERE
(dbo.Crew_Category.Description = N'Flight Deck' OR
dbo.Crew_Category.Description = N'Cabin Crew') AND
(dbo.CrewMember.EmploymentEndDate > #date)
AND dbo.CrewMember.CrewSeqno NOT IN (
SELECT
CrewMember_1.CrewSeqno
FROM
dbo.CrewMember AS CrewMember_1 INNER JOIN
dbo.CrewReqAsg ON CrewMember_1.CrewSeqno = dbo.CrewReqAsg.crewSeqno INNER JOIN
dbo.activity ON dbo.CrewReqAsg.act_seqno = dbo.activity.act_seqno INNER JOIN
dbo.ActivityType ON dbo.activity.actType_seqno = dbo.ActivityType.actType_seqno INNER JOIN
dbo.ActivityCategory ON dbo.ActivityType.ActCat_seqno = dbo.ActivityCategory.actCat_seqno INNER JOIN
dbo.Crew_Category AS Crew_Category_1 ON CrewMember_1.CrewCategorySeqNo = Crew_Category_1.CrewCategorySeqno
WHERE (
dbo.ActivityCategory.Category = N'Ferie' OR
dbo.ActivityCategory.Category = N'Fridage' OR
dbo.ActivityCategory.Category = N'Sygdom') AND (Crew_Category_1.Description = N'Flight Deck' OR
Crew_Category_1.Description = N'Cabin Crew') AND (LEFT(dbo.activity.Start,10) LIKE #date));
SET #returnVal = 'FlightDeck:'+CAST(#flightDeck AS varchar);
SET #returnVal += ',CabinCrew:'+CAST(#cabinCrew AS varchar);
END
RETURN #returnVal
It's been a while since I've had to do this so perhaps I just forgot something fundamental. Please help me figure out why this happens? :)
Yes, you forgot something fundamental. To return data to the caller, use SELECT, not RETURN.
You need
SELECT #returnVal

Modifying XML Column with Select Query

I have a SQL Server table with a XML column which got information in. I want to select whole ID's from this table and modify my another xml column.
My query is;
declare #name nvarchar(max);
set #name = 'mark';
update table1
set table1.Information1.modify('insert <s n="' + cast((select cast(table2.Information2 as varchar(100))
from table2
where table2.Information2.exist('/r/s[#n=sql:variable("#name")]') = 1) as varchar(400)) + '"/> into (/r)[1]') where table1.Name = #name;
I'm getting
Msg 8172, Level 16, State 1, Line 5
The argument 1 of the XML data type method "modify" must be a string literal.
Any help would be nice.
Are you sure you want to put the whole xml into attribute of Information1, like this:
declare #name nvarchar(max), #data xml
select #name = 'mark'
select cast(Information2 as varchar(100)) as n
from table2 as t
where t.Information2.exist('/r/s[#n=sql:variable("#name")]') = 1
for xml raw('s')
update table1 set Information1.modify('insert sql:variable("#data") into (/r)[1]')
sql fiddle demo
Much as you are doing with the filtering, you need to use sql:variable. You can't build up a string inside a .modify function.
declare #newData xml
select #newData = '<s n="' +
cast(table2.Information2 as varchar(100))
+ '"/>'
from table2
where table2.Information2.exist('/r/s[#n=sql:variable("#name")]') = 1
update table1 set Information1.modify('insert sql:variable("#newData") into (/r)[1]')

SQL WHERE ... IN clause with possibly null parameter

I am having some problems with my WHERE clause (using SQL 2008) . I have to create a stored procedure that returns a list of results based on 7 parameters, some of which may be null. The ones which are problematic are #elements, #categories and #edu_id. They can be a list of ids, or they can be null. You can see in my where clause that my particular code works if the parameters are not null. I'm not sure how to code the sql if they are null. The fields are INT in the database.
I hope my question is clear enough. Here is my query below.
BEGIN
DECLARE #elements nvarchar(30)
DECLARE #jobtype_id INT
DECLARE #edu_id nvarchar(30)
DECLARE #categories nvarchar(30)
DECLARE #full_part bit
DECLARE #in_demand bit
DECLARE #lang char(2)
SET #jobtype_id = null
SET #lang = 'en'
SET #full_part = null -- full = 1, part = 0
SET #elements = '1,2,3'
SET #categories = '1,2,3'
SET #edu_id = '3,4,5'
select
jobs.name_en,
parttime.fulltime_only,
jc.cat_id category,
je.element_id elem,
jt.name_en jobtype,
jobs.edu_id minEdu,
education.name_en edu
from jobs
left join job_categories jc
on (jobs.job_id = jc.job_id)
left join job_elements je
on (jobs.job_id = je.job_id)
left join job_type jt
on (jobs.jobtype_id = jt.jobtype_id)
left join education
on (jobs.edu_id = education.edu_id)
left join
(select job_id, case when (jobs.parttime_en IS NULL OR jobs.parttime_en = '') then 1 else 0 end fulltime_only from jobs) as parttime
on jobs.job_id = parttime.job_id
where [disabled] = 0
and jobs.jobtype_id = isnull(#jobtype_id,jobs.jobtype_id)
and fulltime_only = isnull(#full_part,fulltime_only)
-- each of the following clauses should be validated to see if the parameter is null
-- if it is, the clause should not be used, or the SELECT * FROM ListToInt... should be replaced by
-- the field evaluated: ie if #elements is null, je.element_id in (je.element_id)
and je.element_id IN (SELECT * FROM ListToInt(#elements,','))
and jc.cat_id IN (SELECT * FROM ListToInt(#categories,','))
and education.edu_id IN (SELECT * FROM ListToInt(#edu_id,','))
order by case when #lang='fr' then jobs.name_fr else jobs.name_en end;
END
Something like
and (#elements IS NULL OR je.element_id IN
(SELECT * FROM ListToInt(#elements,',')))
and (#categories IS NULL OR
jc.cat_id IN (SELECT * FROM ListToInt(#categories,',')))
....
should do the trick
je.element_id IN (SELECT * FROM ListToInt(#elements,',')) OR #elements IS NULL
that way for each one
Have you tried explicitly comparing to NULL?
and (#elements is null or je.element_id IN (SELECT * FROM ListToInt(#elements,','))
And so on.

Convert Comma Delimited String to bigint in SQL Server

I have a varchar string of delimited numbers separated by commas that I want to use in my SQL script but I need to compare with a bigint field in the database. Need to know to convert it:
DECLARE #RegionID varchar(200) = null
SET #RegionID = '853,834,16,467,841,460,495,44,859,457,437,836,864,434,86,838,458,472,832,433,142,154,159,839,831,469,442,275,840,299,446,220,300,225,227,447,301,450,230,837,441,835,302,477,855,411,395,279,303'
SELECT a.ClassAdID, -- 1
a.AdURL, -- 2
a.AdTitle, -- 3
a.ClassAdCatID, -- 4
b.ClassAdCat, -- 5
a.Img1, -- 6
a.AdText, -- 7
a.MemberID, -- 9
a.Viewed, -- 10
c.Domain, -- 11
a.CreateDate -- 12
FROM ClassAd a
INNER JOIN ClassAdCat b ON b.ClassAdCAtID = a.ClassAdCAtID
INNER JOIN Region c ON c.RegionID = a.RegionID
AND a.PostType = 'CPN'
AND DATEDIFF(d, GETDATE(), ExpirationDate) >= 0
AND a.RegionID IN (#RegionID)
AND Viewable = 'Y'
This fails with the following error:
Error converting data type varchar to bigint.
RegionID In the database is a bigint field.. need to convert the varchar to bigint.. any ideas..?
Many thanks in advance,
neojakey
create this function:
CREATE function [dbo].[f_split]
(
#param nvarchar(max),
#delimiter char(1)
)
returns #t table (val nvarchar(max), seq int)
as
begin
set #param += #delimiter
;with a as
(
select cast(1 as bigint) f, charindex(#delimiter, #param) t, 1 seq
union all
select t + 1, charindex(#delimiter, #param, t + 1), seq + 1
from a
where charindex(#delimiter, #param, t + 1) > 0
)
insert #t
select substring(#param, f, t - f), seq from a
option (maxrecursion 0)
return
end
change this part:
AND a.RegionID IN (select val from dbo.f_split(#regionID, ','))
Change this for better overall performance:
AND DATEDIFF(d, 0, GETDATE()) <= ExpirationDate
Your query does not know that those are separate values, you can use dynamic sql for this:
DECLARE #RegionID varchar(200) = null
SET #RegionID = '853,834,16,467,841,460,495,44,859,457,437,836,864,434,86,838,458,472,832,433,142,154,159,839,831,469,442,275,840,299,446,220,300,225,227,447,301,450,230,837,441,835,302,477,855,411,395,279,303'
declare #sql nvarchar(Max)
set #sql = 'SELECT a.ClassAdID, -- 1
a.AdURL, -- 2
a.AdTitle, -- 3
a.ClassAdCatID, -- 4
b.ClassAdCat, -- 5
a.Img1, -- 6
a.AdText, -- 7
a.MemberID, -- 9
a.Viewed, -- 10
c.Domain, -- 11
a.CreateDate -- 12
FROM ClassAd a
INNER JOIN ClassAdCat b ON b.ClassAdCAtID = a.ClassAdCAtID
INNER JOIN Region c ON c.RegionID = a.RegionID
AND a.PostType = ''CPN''
AND DATEDIFF(d, GETDATE(), ExpirationDate) >= 0
AND a.RegionID IN ('+#RegionID+')
AND Viewable = ''Y'''
exec sp_executesql #sql
I use this apporach sometimes and find it very good.
It transfors your comma-separated string into an AUX table (called #ARRAY) and then query the main table based on the AUX table:
declare #RegionID varchar(50)
SET #RegionID = '853,834,16,467,841,460,495,44,859,457,437,836,864,434,86,838,458,472,832,433,142,154,159,839,831,469,442,275,840,299,446,220,300,225,227,447,301,450,230,837,441,835,302,477,855,411,395,279,303'
declare #S varchar(20)
if LEN(#RegionID) > 0 SET #RegionID = #RegionID + ','
CREATE TABLE #ARRAY(region_ID VARCHAR(20))
WHILE LEN(#RegionID) > 0 BEGIN
SELECT #S = LTRIM(SUBSTRING(#RegionID, 1, CHARINDEX(',', #RegionID) - 1))
INSERT INTO #ARRAY (region_ID) VALUES (#S)
SELECT #RegionID = SUBSTRING(#RegionID, CHARINDEX(',', #RegionID) + 1, LEN(#RegionID))
END
select * from your_table
where regionID IN (select region_ID from #ARRAY)
It avoids you from ahving to concatenate the query string and then use EXEC to execute it, which I dont think it is a very good approach.
if you need to run the code twice you will need to drop the temp table
I think the answer should be kept simple.
Try using CHARINDEX like this:
DECLARE #RegionID VARCHAR(200) = NULL
SET #RegionID =
'853,834,16,467,841,460,495,44,859,457,437,836,864,434,86,838,458,472,832,433,142,154,159,839,831,469,442,275,840,299,446,220,300,225,227,447,301,450,230,837,441,835,302,477,855,411,395,279,303'
SELECT 1
WHERE Charindex('834', #RegionID) > 0
SELECT 1
WHERE Charindex('999', #RegionID) > 0
When CHARINDEX finds the value in the large string variable, it will return it's position, otherwise it return 0.
Use this as a search tool.
The easiest way to change this query is to replace the IN function with a string function. Here is what I consider the safest approach using LIKE (which is portable among databases):
AND ','+#RegionID+',' like '%,'+cast(a.RegionID as varchar(255))+',%'
Or CHARINDEX:
AND charindex(','+cast(a.RegionID as varchar(255))+',', ','+#RegionID+',') > 0
However, if you are explicitly putting the list in your code, why not use a temporary table?
declare #RegionIds table (RegionId int);
insert into #RegionIds
select 853 union all
select 834 union all
. . .
select 303
Then you can use the table in the IN clause:
AND a.RegionId in (select RegionId from #RegionIds)
or in a JOIN clause.
I like Diego's answer some, but I think my modification is a little better because you are declaring a table variable and not creating an actual table. I know the "in" statement can be a little slow, so I did an inner join since I needed some info from the Company table anyway.
declare #companyIdList varchar(1000)
set #companyIdList = '1,2,3'
if LEN(#companyIdList) > 0 SET #companyIdList = #companyIdList + ','
declare #CompanyIds TABLE (CompanyId bigint)
declare #S varchar(20)
WHILE LEN(#companyIdList) > 0 BEGIN
SELECT #S = LTRIM(SUBSTRING(#companyIdList, 1, CHARINDEX(',', #companyIdList) - 1))
INSERT INTO #CompanyIds (CompanyId) VALUES (#S)
SELECT #companyIdList = SUBSTRING(#companyIdList, CHARINDEX(',', #companyIdList) + 1, LEN(#companyIdList))
END
select d.Id, d.Name, c.Id, c.Name
from [Division] d
inner join [Company] c on d.CompanyId = c.Id
inner join #CompanyIds cids on c.Id = cids.CompanyId