converting nvarchar to int error - sql

I have this query that I can't quite work out the error I'm getting.
Conversion failed when converting the nvarchar value '1.5' to data type int.
This is due to pvlaue for one case pulling a '1.5' string and trying to convert it to an int implicitly. I tried to rectify this by casting to a float but this doesn't seem to work for me. Any help in this regard would be very helpful.
I'm running this on SQL Server 2012 Management Studio.
BEGIN DECLARE #Item NVARCHAR(100)
SET #Item = 'Water'
SELECT
d.DESCRIPTION, d.ITEM_CODE,
el.ENUM_LABEL as Building_Block,
CAST(itpC.pvalue AS DECIMAL(22, 3)), ffi.where_used,
d.STATUS_IND
FROM
FSITEM d
LEFT JOIN
FSITEMTECHPARAM itp ON d.ITEM_CODE = itp.ITEM_CODE
AND itp.PARAM_CODE = 'BUILDING_BLOCK'
LEFT JOIN
FSVALIDENUMVALCF ev ON CAST(coalesce(itp.PVALUE, 0) as float) = CAST(ev.ENUM_VALUE AS FLOAT)
AND ev.ENUM_CODE = 'C_BUILDING_BLOCK_TYP'
LEFT JOIN
FSVALIDENUMLABELCF el ON ev.ENUM_CODE = el.ENUM_CODE
AND ev.ENUM_ORDER = el.ENUM_ORDER
AND el.LANGUAGE_CODE = 'EN-US'
LEFT JOIN
(SELECT
item_Code, max(CAST(pvalue AS FLOAT)) as pvalue
FROM
FSITEMTECHPARAM
JOIN
KC_SITE_COST_MAP ON FSITEMTECHPARAM.PARAM_CODE = 'COST_'+KC_SITE_COST_MAP.COST_CODE
GROUP BY
ITEM_CODE
UNION
SELECT
ITEM_CODE, CAST(pvalue AS FLOAT) as pvalue
FROM
FSFORMULA
JOIN
FSFORMULATECHPARAM ON FSFORMULA.FORMULA_ID = FSFORMULATECHPARAM.FORMULA_ID
AND PARAM_CODE = 'COST_TOTAL_BASE'
WHERE
FSFORMULA.FORMULA_ID IN (SELECT FORMULA_ID FROM FSITEM)) itpC ON D.item_code = itpC.item_code
LEFT JOIN
(SELECT
ffi.item_code, COUNT(distinct ffi.formula_id) where_used
FROM
fsformulaingred ffi
JOIN
fsformula ff ON ffi.formula_id = ff.formula_id
WHERE
ff.status_ind = 500 AND ff.logical_delete = 0
GROUP BY
ffi.item_code) ffi ON D.item_code = ffi.item_code
WHERE
d.LOGICAL_DELETE = 0
AND d.COMPONENT_IND <> 2
AND d.Status_IND < 600
AND (((CAST(ev.ENUM_VALUE as FLOAT) = 3 or CAST(ev.ENUM_VALUE as FLOAT) = 2)
AND d.Status_Ind < 600) OR
CAST(ev.ENUM_VALUE as FLOAT) = 1 or CAST(ev.ENUM_VALUE as FLOAT) = '0')
AND (d.Description Like #Item + '%')
--Or d.Description Like replace(#Item, '!', '%'))
ORDER BY
CAST(coalesce(ev.enum_value,0) as FLOAT) DESC, status_ind DESC, d.Item_Code
END

It's the coalesce that is doing the conversion to int, so casting the result from the coalesce is too late.
The result of an expression has to have the same type regardless of how the expression is evaluated. The expression coalesce(itp.PVALUE, 0) will be of the type int regardless of whether the value itp.PVALUE or the value 0 is used, because the type of 0 is int.
If you cast the 0 to float, the type of the expression will be float, so use coalesce(itp.PVALUE, cast(0 as float)).

Related

SQL How to count all remains for each date

I have the following SQL function
CREATE FUNCTION [dbo].[GetCardDepartRemains]
(
#CardId INT,
#DepartId INT,
#Date DATETIME = NULL,
#DocumentId INT = NULL
)
RETURNS INT
AS
BEGIN
DECLARE #Res INT
SELECT
#Res = ISNULL(SUM(CASE WHEN Operations.[Output] = 0 AND Operations.RecipientId = #DepartId THEN 1 ELSE -1 END), 0)
FROM dbo.Operations
WHERE Operations.CardId = #CardId
AND (Operations.[Output] = 0 AND Operations.RecipientId = #DepartId OR Operations.Input = 0 AND Operations.SenderId = #DepartId)
AND (#Date IS NULL OR Operations.[Date] <= #Date)
RETURN #Res
END
GO
It counts remains for certain product on certain department on certain date.
If it is less then zero it means something's wrong with database
Now I need to find all remains for each card, for each department for all dates in database where result is wrong.
Theoretically speaking we can fing this by calling this procedure in a query like this
SELECT DISTINCT Operations.[Date] as [Date],
Departments.Id as Depart,
Cards.Id as [Card],
[dbo].[GetCardDepartRemains] (Cards.Id, Departments.Id,Operations.[Date],NULL) as Res
FROM [jewellery].[dbo].[Cards]
CROSS JOIN [jewellery].[dbo].[Departments]
CROSS JOIN [jewellery].[dbo].[Operations]
WHERE [dbo].[GetCardDepartRemains] (Cards.Id, Departments.Id,Operations.[Date],NULL) = -1
But this query executes more than 2 minutes, so we need to write a new query.
My query can find all remains for each card on each department on certain date (ex. '2016-10-04')
SELECT
[Card],
Depart,
Res
FROM
(SELECT
Cards.Id as [Card],
Departments.Id as Depart,
ISNULL(SUM(CASE WHEN Operations.[Output] = 0 AND Operations.RecipientId = Departments.Id THEN 1 ELSE -1 END), 0) as Res
FROM Operations
CROSS JOIN Cards
CROSS JOIN Departments
WHERE Operations.CardId = Cards.Id
AND (Operations.[Output] = 0 AND Operations.RecipientId = Departments.Id OR Operations.Input = 0 AND Operations.SenderId = Departments.Id)
AND (Operations.[Date] <= '2016-10-04')
GROUP BY Cards.Id, Departments.Id
) as X
WHERE Res = -1
Can you help to re-write this query to find remains for all dates?
Assuming SQL Server is 2008 or above:
To find all dates, just comment out the date filter like this:
-- AND (Operations.[Date] <= '2016-10-04')
If you need to filter on a date range:
AND (Operations.[Date] between between getDate()-30 and getDate()
Changing -30 to however many days in the past. So a year ago would be -364.

SQL Server 2008 R2 fails on cast varchar->time

I encountered a weird error in our SQL Server 2008 R2 server. The cast of a varchar to time fails depending on what other columns are used in SELECT clause of the top level statement. Code to reproduce the issue
CREATE FUNCTION [dbo].[explode]
(
#haystack varchar(max),
#separator varchar(8000)
)
RETURNS
#ret TABLE
(
orderCol int identity(1,1),
value varchar(max)
)
AS
BEGIN
declare #index bigint
set #index = charindex(#separator,#haystack)
while #index > 0
begin
insert into #ret (value) values (substring(#haystack,1,#index-1))
set #haystack = substring(#haystack,#index+len(#separator),len(#haystack)-#index-len(#separator)+1)
set #index = charindex(#separator,#haystack)
end
insert into #ret (value) select #haystack
RETURN
END
And the query:
declare #s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare #units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set #units = '#label,#date,#hour,'+#units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(#s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(#units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,convert(varchar,(case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end+':00'),108) as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '#label' and c2.unit = '#date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '#hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('#label','#date','#hour')
)
select unit, label, [date], value, cast([time] as time)
from topCte
This will fail with:
Msg 241, Level 16, State 1, Line 5
Conversion failed when converting date and/or time from character string.
However when I change the last two lines into any of these, it works correctly:
select unit, label, [date], value, [time]
from topCte
select unit, label, [date], cast([time] as time)
from topCte
I would like to stress that I'm fully aware that this code is sub optimal and I know how to rework this so to avoid the error by rewriting the code still fullfilling business requirement. However this error shouldn't occur in this way and I'm very curious what is triggering it.
I believe there is something wrong in how SQL Server runs the upper query.
I would store the result into a Temporary Table and then cast the result as desired
create table #table_name
(
unit varchar(3),
label varchar(10),
[date] varchar(10),
value varchar(10),
[time] varchar(10),
)
declare #s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare #units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set #units = '#label,#date,#hour,'+#units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(#s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(#units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,(case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end+':00') as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '#label' and c2.unit = '#date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '#hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('#label','#date','#hour')
)
insert into #table_name(unit, label, [date], value, [time])
select unit, label, [date], value, [time]
from topCte
select unit, label, [date], value, cast([time] as time)
from #table_name
The problem is your case expression for the [time] column. If you remove the cast in the select statement you will see values like 14:51:00 which is invalid for time. You are initially converting to a varchar but you don't specify the size which is another issue. There really is no need to convert this to a varchar because it is already a varchar value.
Here is a working version of your code.
declare #s varchar(1000) = 'a,2015-10-08,1451,1,2,3,4,5,6,7,8,9,10,11;a,2015-10-08,1721,12,13,14,15,16,17,18,19,20,21,22'
declare #units varchar(1000) = 'l1,l2,l3,l4,l5,l6,l7,l8,l9,l10,l11'
set #units = '#label,#date,#hour,'+#units
;with cte as (select b.value,c.value as unit, a.orderCol as ri from dbo.explode(#s,';') a
cross apply dbo.explode(value,',') b
inner join dbo.explode(#units,',') c
on b.orderCol = c.orderCol),
topCte as (
select c4.unit as unit
,
case
when len(c3.value) <= 3 then '0' + substring(c3.value,1,1) + ':' + substring(c3.value,2,2)
else (substring(c3.value,1,2) + ':' + substring(c3.value,3,2))
end
as [time]
,c1.value as label
,c2.value as [Date]
,c4.value
from cte c1
inner join cte c2
on c1.ri = c2.ri and c1.unit = '#label' and c2.unit = '#date'
inner join cte c3
on c1.ri = c3.ri and c3.unit = '#hour'
inner join cte c4
on c1.ri = c4.ri and c4.unit not in ('#label','#date','#hour')
)
select unit, label, [date], value, cast([time] as time)
from topCte

tsql arithmetic overflow converting expression to data type int only with adodb

with the application I am working on I get this error:
error '80040e57'
Arithmetic overflow error converting expression to data type int
when this query:
SELECT 0 as type_elm
, 999 AS key0
, def_car.desc_elm as sh_elm
, def_car.desc_elm
, id_prd = CAST((CASE WHEN def_key0.key0 IS NULL AND def_car.desc_elm <> 'Non Perfezionati'
THEN CAST((-100 * def_ger.id_prd) as bigint) ELSE (-100 * def_ger.id_cld) + (ISNULL(def_key0.key0,9) * 10) END) AS BIGINT)
, id_cld = CAST((CASE WHEN def_ger.id_cld > 0 THEN CAST((-100 * def_ger.id_cld) as bigint) ELSE 0 END ) AS BIGINT)
,CAST(0 AS BIGINT) as id_prd_real
,CAST(0 AS BIGINT) as cessato_area
, cessato = CAST((CASE WHEN def_ger.id_cld = 0 THEN def_ger.id_prd ELSE 100 - (def_key0.key0 * 10) END) AS BIGINT)
,cessato_orig = CAST((CASE WHEN def_ger.id_cld = 0 THEN def_ger.id_prd ELSE 0 END) AS BIGINT) FROM def_car
INNER JOIN def_ger ON def_car.id_prd = def_ger.id_prd
LEFT JOIN def_key0 ON def_key0.desc_key0 = def_car.desc_elm
WHERE (def_ger.key0 = 999)
AND def_ger.key_ndg = 539
AND (def_ger.id_cld = 0 OR def_car.id_prd > 890000000)
AND def_car.desc_elm <> 'Cestino'
UNION SELECT def_tree.type_elm
, 999 as key0
, def_tree.sh_elm
, def_tree.desc_elm
, id_prd = CAST((CASE WHEN def_tree.id_prd > 800000000 THEN def_tree.id_prd
ELSE CAST(CAST(def_car.id_prd as varchar) + RIGHT(('0000' + CAST(def_tree.id_prd as varchar)), 4) as bigint ) END) AS BIGINT)
, id_cld = CAST((CASE WHEN def_tree.id_cld > 800000000 THEN def_tree.id_cld WHEN def_tree.id_cld <= 0 THEN (-100 * def_ger.id_cld) + (def_tree.key0 * 10)
ELSE CAST(CAST(def_car.id_prd as varchar) + RIGHT(('0000' + CAST(def_tree.id_cld as varchar)), 4) as bigint ) END ) AS BIGINT)
, id_prd_real = CAST((CASE WHEN def_tree.id_prd > 0 THEN def_tree.id_prd ELSE def_car.id_prd END ) AS BIGINT)
,CAST(def_car.id_prd AS BIGINT) as cessato_area
, CAST(0 AS BIGINT) as cessato
, CAST(0 AS BIGINT) as cessato_orig FROM def_tree LEFT JOIN def_key0 ON def_tree.key0 = def_key0.key0 LEFT JOIN def_car ON def_key0.desc_key0 = def_car.desc_elm
LEFT JOIN def_ger ON def_ger.id_prd = def_car.id_prd WHERE def_ger.key_ndg = 539 ORDER BY cessato DESC
, id_cld
, type_elm DESC
, desc_elm
is executed in an asp Application with an adoDb object, the wierd think is that this query is executed without problems in SQL Server and after executed into SQL Server the application runs without erro until I clear the cache of SQL Server.I do not understand this issue.
The reason you get the error from ADO but not SSMS is different execution plans. One plan likely evaluates the expression with the problem value earlier in the plan.
Consider changing expressions like this from:
CAST((-100 * def_ger.id_cld) as bigint)
to
CAST(-100 * CAST(def_ger.id_cld as bigint))
so that the intermediate result as well as the final result is the larger type.

Multiply value, round down and CAST Select in SQL

I have a SQL function that gathers labour times from multiple records, and uses STUFF(CAST AS VARCHAR(20)) to concatenate them into a single column in a temp table.
My issue is the labour values in my table are in hours and rounded down to 7 decimals, I want to convert those values to minutes and round them down to 2 decimals before stuffing them into my temp table.
Update: Forgot to mention, I'm using SQL Server 2008 R2
Here is my code.
#site nvarchar(20)
,#item nvarchar(30)
,#enviro nvarchar(30))
RETURNS nvarchar(MAX)
AS
BEGIN
DECLARE #LbrHours AS nvarchar(MAX) = ''
IF #enviro = 'Test'
BEGIN
IF #site = 'Arborg'
BEGIN
SET #LbrHours = STUFF((SELECT ',' + CAST(jrt.run_lbr_hrs AS VARCHAR(20))
FROM Arborg_Test_App.dbo.jobroute AS jbr
INNER JOIN Arborg_Test_App.dbo.job AS job
ON job.job = jbr.job
AND job.suffix = jbr.suffix
INNER JOIN Arborg_Test_App.dbo.item AS itm
ON itm.job = job.job
INNER JOIN Arborg_Test_App.dbo.jrt_sch AS jsh
ON jbr.job = jsh.job
AND jbr.suffix = jsh.suffix
AND jbr.oper_num = jsh.oper_num
LEFT OUTER JOIN Arborg_Test_App.dbo.jrt_sch AS jrt
ON jbr.job = jrt.job
AND jbr.suffix = jrt.suffix
AND jbr.oper_num = jrt.oper_num
WHERE job.suffix = '0' and job.type = 'S' AND itm.item IS NOT NULL
AND itm.item = #item
AND jbr.suffix = CASE -- Return Standard cost if Standard Operation exist, else return current cost
WHEN itm.cost_type = 'S'
THEN '1' -- '1' for standard operation
ELSE '0' -- '0' for current operations
END
ORDER BY itm.item, jbr.oper_num
FOR XML PATH('')), 1, 1, '')
END
END
RETURN #LbrHours
END
jrt.run_lbr_hrs is the column that contains the labour times in hours in our ERP's table. How can I multiply that by 60 and round it down to 2 decimals with in my existing STUFF(CASE AS NVARCHAR)?
CAST to decimal with 2 value after the point and then to varchar
CAST(CAST(jrt.run_lbr_hrs * 60 AS DECIMAL(10, 2)) AS VARCHAR(20))
Change the decimal dimension to what you need
Try this Str (jrt.run_lbr_hrs * 60, 2)

random floating point error with subquery order

I am running sql server 2008 database, i am using the following query in a web app, but for the point of debugging the error i am directly running the query in management studio.
I am getting the following error - An invalid floating point operation occurred. when running this query.
select p.Id as Id, p.CatId as CatId, p.MetaName as MetaName ,p.Active as Active,p.HasChildren as HasChildren ,p.Mlevel as Mlevel ,p.ParentId as ParentId ,p.Type as Type, p.VOrder as VOrder, p.UrlOrder as UrlOrder, Count('*') as VCount
from MetaDataValues as m
left join MetaData as p on m.MetaDataId = p.Id
left join Adverts as a on m.AdvertId = a.Id
where a.Status = 1
and a.ExpDate > current_timestamp and
m.AdvertId in
(select m2.AdvertId from MetaDataValues as m2 left join MetaData as p2 on m2.MetaDataId = p2.Id where p2.MetaName = 'meta1'
and m.AdvertId in (select m3.AdvertId from MetaDataValues as m3 left join MetaData as p3 on m3.MetaDataId = p3.Id where p3.MetaName = 'meta2'
and m.AdvertId in (select m4.AdvertId from MetaDataValues as m4 left join MetaData as p4 on m4.MetaDataId = p4.Id where p4.MetaName = 'meta3'
and m.AdvertId in (select ad9.Id from Adverts as ad9 where dbo.GetDist(ad9.X,ad9.Y,ad9.Z,52.9131514,-2.9313405) < 969))))
group by p.Id, p.CatId, p.MetaName,p.Active,p.HasChildren,p.Mlevel,p.ParentId,p.Type, p.VOrder, p.UrlOrder
To explain its the GetDist function that is causing the problem, if i move this up in the subqueries to the top level the query runs fine?? This isnt an ideal as the code that builds this query is coded in a certain way and i dont want to alter it. so here is the query that works, exactly the same but a different order!
select p.Id as Id, p.CatId as CatId, p.MetaName as MetaName ,p.Active as Active,p.HasChildren as HasChildren ,p.Mlevel as Mlevel ,p.ParentId as ParentId ,p.Type as Type, p.VOrder as VOrder, p.UrlOrder as UrlOrder, Count('*') as VCount
from MetaDataValues as m
left join MetaData as p on m.MetaDataId = p.Id
left join Adverts as a on m.AdvertId = a.Id
where a.Status = 1
and a.ExpDate > current_timestamp and
m.AdvertId in
(select m2.AdvertId from MetaDataValues as m2 left join MetaData as p2 on m2.MetaDataId = p2.Id where p2.MetaName = 'meta1'
and m.AdvertId in (select ad9.Id from Adverts as ad9 where dbo.GetDist(ad9.X,ad9.Y,ad9.Z,52.9131514,-2.9313405) < 969)
and m.AdvertId in (select m3.AdvertId from MetaDataValues as m3 left join MetaData as p3 on m3.MetaDataId = p3.Id where p3.MetaName = 'meta2'
and m.AdvertId in (select m4.AdvertId from MetaDataValues as m4 left join MetaData as p4 on m4.MetaDataId = p4.Id where p4.MetaName = 'meta3' )))
group by p.Id, p.CatId, p.MetaName,p.Active,p.HasChildren,p.Mlevel,p.ParentId,p.Type, p.VOrder, p.UrlOrder
GetDist code
USE [MVC]
GO
/****** Object: UserDefinedFunction [dbo].[GetDist] Script Date: 02/20/2013 17:05:00 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GetDist]
(
#xaxis float,
#yaxis float,
#zaxis float,
#CenterLat float,
#CenterLon float
)
RETURNS float
AS
BEGIN
declare #CntXAxis float
declare #CntYAxis float
declare #CntZAxis float
declare #EarthRadius float
set #EarthRadius = 3961
set #CntXAxis = cos(radians(#CenterLat)) * cos(radians(#CenterLon))
set #CntYAxis = cos(radians(#CenterLat)) * sin(radians(#CenterLon))
set #CntZAxis = sin(radians(#CenterLat))
return (#EarthRadius * acos( #XAxis*#CntXAxis + #YAxis*#CntYAxis + #ZAxis*#CntZAxis))
END
It looks like GetDist may be calculating the distance between a pair of latitude/longitude values. I have a lot of experience with this. Most GetDist functions like this use the Arc Cosine function "ACos". The parameter for this function is limited to the range -1 to 1. If you try to pass a value outside this range, you will get a domain error in SQL Server. If your GetDist function uses a CLR function, the error would be within your .net code and would have a slightly different message.
When dealing with floats, you have to be aware of weird rounding issues. For example, if your calculations would return a value of 1.00000000000001, and you pass that in to the ACos function, you will get an error.
There's a lot of speculation here, and I could be completely off base, but please consider this and spend a couple minutes doing some research.
Based on your GetDist function posted above, I would suggest a relatively minor change:
ALTER FUNCTION [dbo].[GetDist]
(
#xaxis float,
#yaxis float,
#zaxis float,
#CenterLat float,
#CenterLon float
)
RETURNS float
AS
BEGIN
declare #CntXAxis float
declare #CntYAxis float
declare #CntZAxis float
declare #EarthRadius float
declare #Temp float
set #EarthRadius = 3961
set #CntXAxis = cos(radians(#CenterLat)) * cos(radians(#CenterLon))
set #CntYAxis = cos(radians(#CenterLat)) * sin(radians(#CenterLon))
set #CntZAxis = sin(radians(#CenterLat))
Set #Temp = #XAxis*#CntXAxis + #YAxis*#CntYAxis + #ZAxis*#CntZAxis
If #Temp > 1
Set #Temp = 1
Else If #Temp < -1
Set #Temp = -1
return (#EarthRadius * acos(#Temp))
END
Even if this doesn't solve your original problem, it will at least protect you from weird float/precision problems.
Is the problem an invalid operation or something more like "Error converting data type varchar to numeric"? This is a fairly common problem, when numeric data is being stored as a string. It works when the string looks right, but then fails at other times.
Are all the arguments to getDist() of the correct type? Is the return value a number?
I would guess that the filtering at the higher level filters out the bad values that are causing the problem.