Update statement failing without any rows - sql

On a SQL Server 2005 database a stored procedure that had been working starting failing. The following statement in the stored procedure was determined to be the cause of the failure:
update d set
d.location=a.[Name]+'-'+cast(a.lLocaId as varchar)
--select d.logical_name,d.location,a.[Name]+'-'+cast(a.lLocaId as varchar) as location
from hpsmp.dbo.device2m1 d
inner join hpsmp.dbo.locm1 s
on d.location=s.location
inner join hpamp.dbo.amLocation a
on s.location_code=a.lLocaId
where isnumeric(s.location_code)=1
and s.location_name<>a.[Name];
The error message is
Msg 8152, Level 16, State 2, Line 1
String or binary data would be truncated.
The statement has been terminated.
What is odd about this error is that the select statement is not returning any rows. Why would there be a truncation error in an update statement without any rows? There are no triggers on this table.

Your question is really: "What is odd about this error is that the select statement is not returning any rows. Why would there be a truncation error in an update statement without any rows? "
First, when you do the conversion to varchar you should always include a length:
set d.location = a.[Name] + '-' + cast(a.lLocaId as varchar(255))
SQL Server has different default lengths in different contexts, so this could cause a problem. Not the one you are seeing but another one.
I believe that what is happening is that SQL Server constructs the execution plan for the query and it moves the calculation of the new value of location before the filtering. In other words, it can do the calculation while reading hpamp.dbo.amLocation because that is the only table needed for the new value.
Then, it gets an error even on a row that is not being updated.
This is a bit of speculation, but SQL Server does exhibit this behavior in other places. A notorious problem is getting an error when you do:
select cast(col as datetime)
from table t
where isdate(col) = 1;
Yes, this also produces errors on invalid dates and for the same reason.
In your case, I'm not sure what the best way to fix the problem is. You could try something like:
set d.location = (case when len(a.name) < 8 then a.[Name] + '-' + cast(a.lLocaId as varchar(255)) end)
I made up the number 8, but if you set an appropriate value, then it should work.

Related

SQL Server 2012 - Conversion Fails for No Possible Reason with Views

I have a view setup as
CREATE VIEW dbo.my_data_view
AS
SELECT * from dbo.my_used_data (NOLOCK)
UNION
SELECT * from dbo.my_unused_data (NOLOCK)
go
I currently don't have anything in my_unused_data table. But I expect any query made to work just fine. the following is my query:
select * from my_data_view where code = '5E7230893312001084789839'
The query is failing with the message:
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value '8E2230893319020101078983' to data type int.
This doesn't make any sense. my_used_data and my_unused_data tables have the identical schema and types so nothing can go wrong there. What I have found out is that when I have data in my_unused_data. It works fine;Otherwise it fails. Is it something special with the views or what?
Problem solved, the order of the columns where swapped for column no. 3 and 4 for my_unused_data that messed things up. I didn't realise that SQL server assumes that ordering of the column is correct when dealing with SELECT * UNION SELECT * queries

Why does SQL evaluate statements in the true section of an if exists construct, even if the `if exists` returns false?

(I apologize in advance for the awful explanation, but if you run the queries below you should see what I mean!)
Why does MSSQL evaluate statements in the true section of an if exists construct, even if the if exists returns false, causing errors?
For example, in the two queries below, the first checks if a table exists (which it does) and also checks if that table has certain columns. For some reason, running this query throws the following errors because the table exists but the columns don't.
Msg 207, Level 16, State 1, Line 21
Invalid column name 'colB'.
Msg 207, Level 16, State 1, Line 21
Invalid column name 'colC'.
Msg 207, Level 16, State 1, Line 21
Invalid column name 'colA'.
The behavior I expected here was for SQL to just move onto the falsepart of the construct, without throwing errors. (As it does with the next query).
However, the second script (which is identical, bar table names) executes successfully. This is because the table the query is searching for does not exist.
--Scripts to setup the example.
CREATE DATABASE TEST
GO
USE TEST
GO
CREATE TABLE t1 (colD VARCHAR(255)) --Create a table with the correct name, but incorrect column names.
GO
--This query fails, because t1 exists, even though the columns in t1 don't.
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC'))
BEGIN
SELECT colA FROM t1 WHERE colB = 0 AND colC = 1
END
ELSE BEGIN
SELECT 'FALSE'
END
GO
--This query executes ok, because t2 does not exist.
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't2' AND COLUMN_NAME IN ('colA','colB','colC'))
BEGIN
SELECT colA FROM t2 WHERE colB = 0 AND colC = 1
END
ELSE BEGIN
SELECT 'FALSE'
END
Is anybody able to explain to me why the first query errors, when the second query runs fine?
So far, I've only managed to test this in Microsoft SQL Server 2012.
To answer the first part of this question. Assuming familiarity with a language (such as C#) which has some form of runtime type inspection (e.g. Reflection).
Assume you have code like this:
SomeType t = GetSomeTypeFromSomewhere();
if(t.GetType().GetMethod("FunTimes")!=null)
{
t.FunTimes();
}
And assume that SomeType doesn't contain a public method called FunTimes. Even though I've written a guard around trying to invoke the FunTimes method, I get an error. And, specifically, I get a compile time error - the C# compiler cannot even generate the code, let alone get close to running the code, obtaining the result from GetMethod() and deciding not to run the code within the nested block.
To switch back to your code, the exact same type of analysis applies here:
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC'))
BEGIN
SELECT colA FROM t1 WHERE colB = 0 AND colC = 1
END
ELSE BEGIN
SELECT 'FALSE'
END
SQL Server tries to compile this batch and fails. It never executes the code, so it never gets to the point of deciding which branch (IF or ELSE) to take.
So, if all of the above is true, why then does the second piece of code work? That's because of an particular feature of T-SQL called Deferred Name Resolution. Basically, there's a special rule that applies when the object that's missing is a table (or view, since the two are indistinguishable until the object can be found). In that specific instance, SQL Server will not immediately signal a compilation error.
Under deferred name resolution, execution will start and, if something causes schema changes (such as by adding the missing table/view), this causes the system to recompile the remainder of the code.
I think you are evaluating the results wrong (AND it is not your fault IMHO).
EXISTS part returns FALSE in both cases. However, the SQL query parser is funny, it parses the inside expressions and gives the error before execution of the statements, only if column(s) is missing, it doesn't give an error if the table is missing.
In your first query where it seems to be evaluating to TRUE, try changing table name to something like t2 and you would see it runs and evaluates to FALSE in both.

DB2 SQL Updating a table value

I'm trying to update a table value that is initially set to 0, I'm working with DB2. However, when I go to execute my SQL I get the following error:
DSNT408I SQLCODE = -406, ERROR: A CALCULATED OR DERIVED NUMERIC VALUE IS NOT
WITHIN THE RANGE OF ITS OBJECT COLUMN
DSNT418I SQLSTATE = 22003 SQLSTATE RETURN CODE
I understand what the error means, but I do not understand why I am getting it. Here is my SQL:
UPDATE INTTABLE
SET PAYMENT = DECIMAL((MONTHIRATE*OMA)/(1-POWER(1+MONTHIRATE,-420)),8,2);
Where PAYMENT is defined as DECIMAL(8,2)
Could someone please explain to me why the above UPDATE statement will not work?
Probably what is happening is that the calculation you are doing is somewhere getting a result with more than 6 digits before the decimal place.
DB2 will handle having more numbers after the decimal place than you have defined in the SCALE, but it will error (with the -406 you are seeing) when there are more digits than allowed with the PRECISION defined.
Just as an aside, do make sure you realize that a DECIMAL(8,2) will give you 6 places before the decimal and 2 after.
Edit: I think this query will show you the offending row(s):
SELECT * FROM (
SELECT
A.*
,(MONTHIRATE*OMA)/(1-POWER(1+MONTHIRATE,-420)) AS CALC
FROM INTTABLE A
) B
WHERE CALC > 999999

Error when summing converted column

I have a table in SQL Server that contains 2 columns: Question_asked, Response
The Response column is of the datatype varchar(500) and holds the users response to the question that is held in the column Question_asked
I have created a view V_age_attr with the following SQL statement:
select
question_asked, cast(Response as integer)
from
main_table
where
question_asked = 'What is your age'
If I run a simple SQL query the results return as expected. But in some circumstances like when using a sum/group by query. I get an error msg (actual msg I get):
Conversion failed when converting the varchar value '2011-08-13 00:00:00' to data type int
In the table, main_table, one of the questions is the start_date, so the column Response does hold date values in the table, but not in the view I created.
Again, I can run a select query and check all the values for my cast(Response as integer) and they all are integer values.
My main question is: can anyone tell me if this is known/expected behavior in SQL Server 2008 R2, and/or does anyone else have another way to get a subset of values from this type of table other than creating a view?
Thank you
Alas, filtering by isnumeric doesn't necessarily work. SQL is a descriptive language, not a procedural language. So, the operations do not necessary work in the order specified.
In this case, SQL Server will attempt to do the conversion when reading the data and then apply the filter. Too late. You have an error.
The case statement is the only statement that guarantees order of evaluation, at least when aggregation functions are not involved. The following should fix the conversion problem:
select question_asked,
(case when isnumeric(Response) = 1 and
Response not like '%.%'
then cast(Response as int)
end) as Response
from main_table
where question_asked = 'What is your age'
This version also checks for a decimal point in Response. ISNUMERIC will return 1 when there is a decimal point, but that will fail the conversion as well.
Given the bug that Martin has highlighted, I would actually use the original condition in the CASE statement, which will apply to any numeric type, not limited to Gordon's int interpretation. Better than ISNUMERIC, but TRY_CONVERT would be better from SQL Server 2012 onwards. You would hope SQL Server can re-use the condition resolution in the WHERE clause to assist in the SELECT, or vice-versa.
select
question_asked, case when question_asked = 'What is your age'
then cast(Response as integer)
end
from
main_table
where
question_asked = 'What is your age'
Filter using ISNUMERIC - there may be non-integral data as responses which are messing up the aggregate functions.
SELECT
question_asked,
CASE(response AS INT)
FROM
main_table
WHERE
question_asked = 'What is your age'
AND ISNUMERIC(response) = 1
Edit: if that still doesn't work, you may need to do some more aggressive type-checking on your response column. Check out the responses here:
How to get if a string is a number in T-SQL

SQL: Error, Expression services limit reached?

"Internal error: An expression services limit has been reached. Please look for potentially complex expressions in your query, and try to simplify them."
Has anyone seen this before and found a good workaround?
I managed to get around this issue by splitting my SQL query into two parts essentially and writing the first SQL select query to a temp table and the second part, a new SQL select statement selects from the temporary table and uses alot of CROSS APPLY operator to Calculate cascading computed columns.
This is an example of how the second part looks but I'm using alot more Cross Applys to produce new columns which are calculations:
Select * from #tempTable
cross apply
(
select HmmLowestSalePrice =
round(((OurSellingPrice + 1.5) / 0.95) - (CompetitorsLowestSalePrice) + 0.08, 2)
) as HmmLowestSalePrice
cross apply
(
select checkLowestSP =
case
when adjust = 'No Room' then 'No Room'
when OrginalTestSalePrice >= CompetitorsLowestSalePrice then 'Minus'
when OrginalTeslSalePrice < CompetitorsLowestSalePrice then 'Ok'
end
) as checkLowestSP
cross apply
(
select AdjustFinalNewTestSP =
case
when FinalNewTestShipping < 0 Then NewTestSalePrice - (FinalNewTestShipping)
when FinalNewTestShipping >= 0 Then NewTestSalePrice
end
) as AdjustFinalNewTestSP
cross apply
(
select CheckFinalSalePriceWithWP =
case
when round(NewAdminSalePrice, 2) >= round(wholePrice, 2) then 'Ok'
when round(NewAdminSalePrice, 2) < round(wholePrice, 2) then 'Check'
end
) as CheckFinalPriceWithWP
DROP TABLE #tempTable
My goal to to put this into a sql report and it work fine if there is 1 user only as the #tempTable will get created and dropped in the same execution and the results are displayed in the report correctly. But in the future if there are concurrent users I'm concerned that they will be writing to the same #tempTable which will affect the results?
I've looked at putting this into stored procedures but still get the error message above.
This issue occurs because SQL Server limits the number of identifiers and constants that can be contained in a single expression of a query. The limit is 65,535. The test for the number of identifiers and constants is performed after SQL Server expands all referenced identifiers and constants. In SQL Server 2005 and above, queries are internally normalized and simplified. And that includes *(asterisk), computed columns etc.
In order to work around this issue, rewrite your query. Reference fewer identifiers and constants in the largest expression in the query. You must make sure that the number of identifiers and constants in each expression of the query does not exceed the limit. To do this, you may have to break down a query into more than one single query. Then, create a temporary intermediate result.
The same issue happens to me when we tried to change the Database Compatibility Level to 150. It is not an issue when it is 140 or lower.
I just had this problem and fixed it by removing the UNIQUE index on my table. For some reason, that seems to trigger this error, although it cannot figure out why.
By the way, the same query does work with several other indexes.
What worked for me was replacing several COALESCE statements with ISNULL whenever was possible