Why would YEAR fail with a conversion error from a Date? - sql

I got a view named 'FechasFirmaHorometros' defined as
SELECT IdFormulario,
CONVERT(Date, RValues) AS FechaFirma
FROM dbo.Respuestas
WHERE ( IdPreguntas IN (SELECT IdPregunta
FROM dbo.Preguntas
WHERE
( FormIdentifier = dbo.IdFormularioHorometros() )
AND ( Label = 'SLFYHDLR' )) )
And i have a Function named [RespuestaPreguntaHorometrosFecha] defined as
SELECT Respuestas.RValues
FROM Respuestas
JOIN Preguntas
ON Preguntas.Label = #LabelPregunta
JOIN FechasFirmaHorometros
ON FechasFirmaHorometros.IdFormulario = Respuestas.IdFormulario
WHERE Respuestas.IdPreguntas = Preguntas.IdPregunta
AND YEAR(FechasFirmaHorometros.FechaFirma) = #Anio
AND MONTH(FechasFirmaHorometros.FechaFirma) = #Mes
#LabelPregunta VARCHAR(MAX)
#Anio INT
#Mes INT
I keep getting this message upon hitting the aforementioned function while debugging another stored procedure that uses it
Conversion failed when converting date and/or time from character string.
Yet i can freely do things like
SELECT DAY(FechaFirma) FROM FechasFirmaHorometros
Why is this happening and how can i solve or work around it?

I assume that RValues is a string column of some type, for some reason. You should fix that and store date data using a date data type (obviously in a separate column than this mixed bag).
If you can't fix that, then you can prevent what Damien described above by:
CASE WHEN ISDATE(RValues) = 1 THEN CONVERT(Date, RValues) END AS FechaFirma
(Which will make the "date" NULL if SQL Server can't figure out how to convert it to a date.)
You can't prevent this simply by adding a WHERE clause, because SQL Server will often try to attempt the conversion in the SELECT list before performing the filter (all depends on the plan). You also can't force the order of operations by using a subquery, CTE, join order hints, etc. There is an open Connect item about this issue - they are "aware of it" and "hope to address it in a future version."
Short of a CASE expression, which forces SQL Server to evaluate the ISDATE() result before attempting to convert (as long as no aggregates are present in any of the branches), you could:
dump the filtered results into a #temp table, and then subsequently select from that #temp table, and only apply the convert then.
just return the string, and treat it as a date on the client, and pull YEAR/MONTH etc. parts out of it there
just use string manipulation to pull YEAR = LEFT(col,4) etc.
use TRY_CONVERT() since I just noticed you're on SQL Server 2012:
TRY_CONVERT(DATE, RValues) AS FechaFirma

Related

Exclude '-1.79769313486232E+308' from table using select query in SQL

I want to get the average of the data in one of the table but I get "Arithmetic overflow error converting expression to data type float." exception. My table looks like,
I have to get the average of floating points in the table.
I want to exclude '-1.79769313486232E+308' from the table. How to do that?
Query that I have,
SELECT PS.Name, Z.[Round]
,AVG(Z.[EstimatedValue])
,AVG(Z.[InformationGain])
FROM [dbo].[IntermediateScores] Z
INNER JOIN [dbo].[PScale] PS
ON Z.[ScaleId] = PS.Id
GROUP BY PS.Name, Z.[Round]
ORDER BY PS.Name,Z.[Round]
Assuming that you are running SQL Server (which the syntax of your query and the error message that you showed tend to indicate), you could use TRY_CAST() to handle values that do not fit in the FLOAT datatype. When the conversion fails, TRY_CAST() returns NULL, which aggregate function AVG() ignores.
SELECT
PS.Name,
Z.[Round],
AVG(TRY_CAST(Z.[EstimatedValue] AS FLOAT)),
AVG(TRY_CAST(Z.[InformationGain] AS FLOAT))
FROM [dbo].[IntermediateScores] Z
INNER JOIN [dbo].[PScale] PS ON Z.[ScaleId] = PS.Id
GROUP BY PS.Name, Z.[Round]
ORDER BY PS.Name,Z.[Round]
In general, you want to avoid relying on implicit conversion; it seems like your data is not stored in a numeric datatype, which is the root cause of your problem. Explicit conversion is a better practice, since it is easier to debug when things go wrong.

Error when aggregating due to conversion from varchar to bigint

Situation:
I want to aggregate a value from a table but i get the following error :
Error converting data type varchar to big int.
I've been reading countless of different solutions online but they don't seem to solve it.
Current query;
So based on the error message, i simply added the CAST function but it still doesnt work.
SELECT
base.target_date AS target_date
, base.game_id AS game_id
, base.device AS device
, ISNULL(CAST(SUM(use_point.point) AS bigint),0) AS result
FROM
cte AS base
LEFT JOIN cte2 AS use_point
ON base.target_date = use_point.target_date
AND base.game_id = use_point.device
AND base.device = use_point.device
GROUP BY
base.target_date
, base.device
, base.game_id
WITH ROLLUP
GO
I'm presuming that use_point.point is a VARCHAR, in which case simply change where you put your CAST statement:
, ISNULL(SUM(CAST(use_point.point AS bigint)), 0) AS result
Note that the CAST now takes place before the SUM.
Use TRY_CAST() instead. And it needs to be an argument to the SUM():
SELECT base.target_date, base.game_id, base.device,
COALESCE(SUM(TRY_CAST(use_point.point as bigint)), 0) as result
FROM . . .
Note that your column aliases are redundant, because you are assigning the default aliases.
You should also fix the data. Don't store numeric values as strings. To find the bad data, you can use:
select points
from use_points
where try_convert(points as bigint) is null and
points is not null;

Access SQL Date Function

So I'm working on editing some SQL code and I've just began learning it. I'm trying to fix an update query so it updates a table's value5 column with a corresponding database value. The value type from the database is a number, which I want to convert to a date and place into my table. The database number is in yyyymmdd format so I've been trying to use datefromparts() which doesn't work. Anyone have any ideas?
UPDATE tbl INNER JOIN dB ON
(dB.value1= tbl.value1 OR
dB.value2 =tbl.value2 ) AND
(LEFT(dB.value3 ,5)=tbl.value3 ) AND
(dB.value4 =tbl.value4 )
SET tbl.value5 = DateFromParts(Left(dB.value5,4),Mid(dB.value5,5,2),Right(dB.value5,2))
WHERE tblInvoice.value5 IS NULL;
The current program uses the code
"SET tbl.value5 = dB.value5"
instead (it runs perfectly fine) and I am having another issue with testing the conversion SQL code (datefromparts()). Because I am converting from numbers to time/date, I have to go into the design view of the target table and change the input data type of the value5 column from numbers to time/date. When I run the query with the conversion SQL code, the query stalls for a bit and no values get updated, leaving me with just a blank value5 column. If I now want to fill in the original number values, I change the SQL code back into its original "SET tbl.value5 = dB.value5", change the input data type from time/date to numbers, and rerun the program. The query stalls and no values are updated, and I am again left with blank columns, even though the same code left me with the corrected update values before the modifications to the SQL and table input Data types. I come from a VBA background and I'm just really confused with how this is working. Any tips would be appreciated, thanks!
Have you tried with substring instead?
SELECT DATEFROMPARTS ( left('20101231',4), substring('20101231',5,2), right('20101231',2) ) AS Result;
MS Access (and MS Jet too) have no DateFromParts function. Using DateSerial instead.
SET tbl.value5 = DateSerial(Left(dB.value5, 4), Mid(dB.value5, 5, 2), Right(dB.value5, 2))
It's not clear if you work with T-SQL or Access SQL. In Access, you can use Format:
SET tbl.value5 = CDate(Format(dB.value5, "####\/##\/##"))
In T-SQL you could use a similar method.

How does one filter based on whether a field can be converted to a numeric?

I've got a report that has been in use quite a while - in fact, the company's invoice system rests in a large part upon this report (Disclaimer: I didn't write it). The filtering is based upon whether a field of type VarChar(50) falls between two numeric values passed in by the user.
The problem is that the field the data is being filtered on now not only has simple non-numeric values such as '/A', 'TEST' and a slew of other non-numeric data, but also has numeric values that seem to be defying any type of numeric conversion I can think of.
The following (simplified) test query demonstrates the failure:
Declare #StartSummary Int,
#EndSummary Int
Select #StartSummary = 166285,
#EndSummary = 166289
Select SummaryInvoice
From Invoice
Where IsNull(SummaryInvoice, '') <> ''
And IsNumeric(SummaryInvoice) = 1
And Convert(int, SummaryInvoice) Between #StartSummary And #EndSummary
I've also attempted conversions using bigint, real and float and all give me similar errors:
Msg 8115, Level 16, State 2, Line 7
Arithmetic overflow error converting
expression to data type int.
I've tried other larger numeric datatypes such as BigInt with the same error. I've also tried using sub-queries to sidestep the conversion issue by only extracting fields that have numeric data and then converting those in the wrapper query, but then I get other errors which are all variations on a theme indicating that the value stored in the SummaryInvoice field can't be converted to the relevant data type.
Short of extracting only those records with numeric SummaryInvoice fields to a temporary table and then querying against the temporary table, is there any one-step solution that would solve this problem?
Edit: Here's the field data that I suspect is causing the problem:
SummaryInvoice
11111111111111111111111111
IsNumeric states that this field is numeric - which it is. But attempting to convert it to BigInt causes an arithmetic overflow. Any ideas? It doesn't appear to be an isolated incident, there seems to have been a number of records populated with data that causes this issue.
It seems that you are gonna have problems with the ISNUMERIC function, since it returns 1 if can be cast to any number type (including ., ,, e0, etc). If you have numbers longer than 2^63-1, you can use DECIMAL or NUMERIC. I'm not sure if you can use PATINDEX to perform an regex look on SummaryInvoice, but if you can, then you should try this:
SELECT SummaryInvoice
FROM Invoice
WHERE ISNULL(SummaryInvoice, '') <> ''
AND CASE WHEN PATINDEX('%[^0-9]%',SummaryInvoice) > 0 THEN CONVERT(DECIMAL(30,0), SummaryInvoice) ELSE -1 END
BETWEEN #StartSummary And #EndSummary
You can't guarantee what order the WHERE clause filters will be applied.
One ugly option to decouple inner and outer.
SELECT
*
FROM
(
Select TOP 2000000000
SummaryInvoice
From Invoice
Where IsNull(SummaryInvoice, '') <> ''
And IsNumeric(SummaryInvoice) = 1
ORDER BY SummaryInvoice
) foo
WHERE
Convert(int, SummaryInvoice) Between #StartSummary And #EndSummary
Another using CASE
Select SummaryInvoice
From Invoice
Where IsNull(SummaryInvoice, '') <> ''
And
CASE WHEN IsNumeric(SummaryInvoice) = 1 THEN Convert(int, SummaryInvoice) ELSE -1 END
Between #StartSummary And #EndSummary
YMMV
Edit: after question update
use decimal(38,0) not int
Change ISNUMERIC(SummaryInvoice) to ISNUMERIC(SummaryInvoice + '0e0')
AND with IsNumeric(SummaryInvoice) = 1, will not short circuit in SQL Server.
But may be you can use
AND (CASE IsNumeric(SummaryInvoice) = 1 THEN Convert(int, SummaryInvoice) ELSE 0 END)
Between #StartSummary And #EndSummary
Your first issue is to fix your database structure so bad data cannot get into the field. You are putting a band-aid on a wound that needs stitches and wondering why it doesn't heal.
Database refactoring is not fun, but it needs to be done when there is a data integrity problem. I assume you aren't really invoicing someone for 11,111,111,111,111,111,111,111,111 or 'test'. So don't allow those values to ever get entered (if you can't change the structure to the correct data type, consider a trigger to prevent bad data from going in) and delete the ones you do have that are bad.

Access 2003 SQL Switch breaks data types?

I'm running Access 2003. I'm using Switch to select date fields based on a boolean criterion:
Switch(<criterion>, Date1, 1, Date2)
i.e., if "criterion" is true, then return Date1, otherwise return Date2.
Date1 and Date2 are Date/Time type columns in a table.
The problem is, Switch returns them as Text -- not Date/Time!
Is there a way to force them into Date-ness? I tried
Switch(<criterion>, #Date1#, 1, #Date2#)
And
Switch(<criterion>, Val(Date1), 1, Val(Date2))
Both of which fail with one error message or another.
Any ideas?
I think the Immediate If [IIf()] function is a better match for what you're trying to do:
IIf(<criterion>, Date1, Date2)
But the Switch() function shouldn't break data types, and is not incompatible with date/time data types. Consider this function:
Public Function trySwitch(ByVal pWhichDay As String) As Variant
Dim varOut As Variant
varOut = Switch(pWhichDay = "yesterday", Date - 1, _
pWhichDay = "today", Date, _
pWhichDay = "tomorrow", Date + 1)
trySwitch = varOut
End Function
trySwitch("today") returns 10/6/2009 and TypeName(trySwitch("today")) returns Date
There's something strange with your example.
Switch accepts expression pairs, and if the first evaluates as True, its paired value is returned, otherwise, it passes on to the second, and evaluates that argument.
You seem to be treating 1 as True, which it is because it's not Fales, but you'd be better off with:
Switch(<criterion>, Date1, True, Date2)
But that's just a replication of the functionality of the Immediate If function, IIf(), and IIf() takes fewer arguments.
But it has the same problem, in that it returns a variant. But you should be able to coerce that to a data type that can be formatted as a Date.
But whether or not that variant will be implicitly coerced or you'll need to do it explicitly, depends on where you're using it. In a query result, you can sort the output from IIf([criterion], Date1, Date2) as a date, because the column gets coerced to date type.
If you have to do the coercion explicitly, CDate() is the function to use -- you'd wrap the outside function that produces Variant output with the CDate() function in order to be certain that the variant output is explicitly coerced to date type:
CDate(IIf(<criterion>, Date1, Date2))
But I might very well be missing something important here, as I appear to be off on a completely different track...
Can you post some code and data to reproduce the problem, please? As this is SWITCH() in SQL code then I think SQL DDL (CREATE TABLE etc) and DML (INSERT INTO to add data) would be most appropriate :)
[Picky point: Access Database SQL does not have a 'boolean' data type. It has a YESNO data type which can be the NULL value; three-value logic is not Boolean.]
Here's some SQL DML (ANSI-92 Query Mode syntax) to demonstrate how it works as expected for me:
SELECT TYPENAME
(
SWITCH
(
NULL, #2009-01-01 00:00:00#,
FALSE, #2009-06-15 12:00:00#,
TRUE, #2009-12-31 23:59:59#
)
);
Change any of the 'criterion' values and the value is always returned as 'Date' i.e. of type DATETIME.
UPDATE:
That TYPENAME function is a great
tool... Access seems to interpret the
entire "column" of the resultset
differently
Indeed. Because a column can only be one data type the results of TYPENAME() at the row can be misleading. Row values of mixed types must be 'promoted' to a higher data type. As is usual with the Access Database Engine, the process is entirely opaque and the documentation on the subject completely absent, so you just have to suck it and see e.g.
SELECT #2009-01-01 00:00:00# AS row_value,
TYPENAME(#2009-01-01 00:00:00#) AS row_type
FROM Customers
UNION ALL
SELECT 0.5,
TYPENAME(0.5) AS row_type
FROM Customers
returns 'Date' and 'Decimal' respectively but what will the column be? Apparently, the answer is:
SELECT DT1.row_value, TYPENAME(DT1.row_value) AS column_type
FROM (
SELECT DISTINCT #2009-01-01 00:00:00# AS row_value
FROM Customers
UNION ALL
SELECT DISTINCT 0.5
FROM Customers
) AS DT1;
'String'?!
...which of course isn't even a Access Database Engine SQL data type. So TYPENAME(), annoyingly, uses the name of the 'best fit' VBA type. For example:
SELECT TYPENAME(CBOOL(0));
returns 'Boolean' even though, as discussed above, there is no Boolean data type in Access Database Engine SQL. And
SELECT TYPENAME(my_binary_col)
returns 'String'. Note the same VBA mapping limitation applies to the CAST functions (yet another annoyance) e.g. there is no 'cast to BINARY' function and the CDEC() function remains broken since Jet 4.0 :(