Understanding SQL Server behaviour with min(datetime) - sql

(long story short, was just a dumb assumption by me, I was sure a column was a datetime and it wasn't - so don't expect to find anything interesting in this question, leaving it here so that dems gets his rightfully accepted answer)
I wrote a simple query like this:
SELECT ID, MIN(DateMadeActive) AS DateMadeActive
FROM RecordStateField WHERE RecordStatusID in (2, 3)
GROUP BY ID
Where DateMadeActive is a datetime column. This returned what I expected:
ID DateMadeActive
1 20/06/2011 16:15:04
2 20/06/2011 16:14:28
Now I inserted this into a new table, but inserting the result of MIN(DateMadeActive) into a datetime column of another table gave me this error:
Msg 8115, Level 16, State 2, Line 1
Arithmetic overflow error converting expression to data type datetime.
So to test this I change my select to this:
SELECT ID, CAST(Min(DateMadeActive) as datetime) AS DateMadeActive
FROM RecordStateField WHERE RecordStatusID in (2, 3)
GROUP BY ID
Same exception. I can write it like this and make it work:
SELECT ID, CONVERT(datetime, Min(DateMadeActive), 103) AS DateMadeActive
FROM RecordStateField WHERE RecordStatusID in (2, 3)
GROUP BY ID
So I can get it working, but this confuses me. The documentation for MIN(expression) says the return type should be the same as expression, but what seems to be happening is that MIN(datetime) is returning nvarchar(255). I can confirm this by running this:
SELECT ID, Min(DateMadeActive) AS DateMadeActive
INTO TestTable
FROM RecordStateField WHERE RecordStatusID in (2, 3)
GROUP BY ID
And I can see that the DateMadeActive column is of type nvarchar(255). Anyone shed any light on this? Just a documentation bug in MSDN?

It looks to me that DateMadeActive in [RecordStateField] is a VARCHAR(255) field. Could you post the table definitions?
I ask this because MIN() has neve rchanged the type for me before. And so if the result is a VARCHAR(255) it seems the input must also be a VARCHAR(255)

Related

Union leads to error converting varchar to numeric

I'm reposting my question from yesterday with the addition of code.
I'm creating a new table LAB_RESULT as a union of two tables (Labs and CTE). A column called result_num (numeric (18,5)) is causing problems in LAB_RESULT.
Table LAB_RESULT is declared first with explicit data types.
CREATE TABLE dbo.LAB_RESULT
(
[LAB_RESULT_CM_ID] VARCHAR (36),
[RESULT_NUM] NUMERIC (18,5)
)
INSERT INTO [dbo].[LAB_RESULT] (LAB_RESULT_CM_ID, RESULT_NUM)
SELECT LAB_RESULT_CM_ID, RESULT_NUM
FROM [etl].[lab_result]
LAB_RESULT draws from varchar columns in tables LABS and CTE. I have used try_cast in both A and B to make sure nothing slips through.
So when I select from (LABS U CTE), I get:
Msg 8114
Error converting data type varchar to numeric
but when I select from only LABS or only CTE (all there just commented out) the data loads fine.
This is happening even if I use cast(null as numeric).
SELECT LAB_RESULT_CM_ID, RESULT_NUM
FROM
((SELECT
NEWID() AS LAB_RESULT_CM_ID,
CAST(NULL AS NUMERIC(18, 5)) AS RESULT_NUM
FROM [dbo].[Labs] as labs
UNION
(SELECT
NEWID() as LAB_RESULT_CM_ID,
CAST(NULL as NUMERIC(18, 5)) AS RESULT_NUM
FROM CTE)
What gives? I would really appreciate some insight. TIA!

Finding max value for a column containing hierarchical decimals

I have a table where the column values are like '1.2.4.5', '3.11.0.6',
'3.9.3.14','1.4.5.6.7', N/A, etc.. I want to find the max of that particular column. However when i use this query i am not getting the max value.
(SELECT max (CASE WHEN mycolumn = 'N/A'
THEN '-1000'
ELSE mycolumn
END )
FROM mytable
WHERE column like 'abc')
I am getting 3.9.3.14 as max value instead of 3.11....
Can someone help me?
Those aren't really decimals - they're strings containing multiple dots, so it's unhelpful to think of them as being "decimals".
We can accomplish your query with a bit of manipulation. There is a type build into SQL Server that more naturally represents this type of structure - hierarchyid. If we convert your values to this type then we can find the MAX fairly easily:
declare #t table (val varchar(93) not null)
insert into #t(val) values
('1.2.4.5'),
('3.11.0.6'),
('3.9.3.14'),
('1.4.5.6.7')
select MAX(CONVERT(hierarchyid,'/' + REPLACE(val,'.','/') + '/')).ToString()
from #t
Result:
/3/11/0/6/
I leave the exercise of fully converting this string representation back into the original form as an exercise for the reader. Alternatively, I'd suggest that you may want to start storing your data using this datatype anyway.
MAX() on values stored as text performs an alphabetic sort.
Use FIRST_VALUE and HIERARCHYID:
SELECT DISTINCT FIRST_VALUE(t.mycolumn) OVER(
ORDER BY CONVERT(HIERARCHYID, '/' + REPLACE(NULLIF(t.mycolumn,'N/A'), '.', '/') + '/') DESC) AS [Max]
FROM #mytable t

Calculating SQL Average with non-numeric data in Table

I have this SQL query that is failing on this nvarchar, even though it seems to me that it is properly guarded for a cast to work. I am trying to get the average of the values for correctly formatted numerical values on a database that I only have read access to.
select TagName, count(TagName) as Freq,
sum(isnumeric(value)) as GoodNums,
avg(case isnumeric(value) when 1 then cast(value as numeric) else 0 end) as "Avg",
Min(Timestamp) as StartTime,
Max(Timestamp) as EndTime
from HH_Data_9 group by TagName
However I never really completely grokked the SQL syntax for combinations of CASE, aggregate functions, and GROUP_BY, so maybe I am just writing it wrong (Tried quite a few things before posting this). Note that the "GoodNums" column works and is giving reasonable answers but when I add the "Avg" column to the query the whole thing errors out of with:
Msg 8114, Level 16, State 5, Line 1
Error converting data type nvarchar to numeric.
This is Microsoft SQL Server 2014 by the way. Any ideas?
Just want to caution that this approach is very dangerous. Consider the following table structure:
CREATE TABLE TestTable
(
FieldType NVARCHAR(50) ,
FieldValue NVARCHAR(50)
)
GO
INSERT INTO dbo.TestTable
VALUES ( 'INT', '5' ),
( 'INT', '15' ),
( 'MONEY', '5.5' )
SELECT *
FROM dbo.TestTable
WHERE FieldType = 'INT'
AND CAST(FieldValue AS INT) > 10
On my machine this query works, but the thing here is that many assumes that this predicate will evaluate from left to right. But this is incorrect. SQL Server engine may decide to evaluate this expression from right to left. And in this case you will get Conversion failed error. It's called ALL-AT-ONCE principle. However in your example this is not applicable, but just wanted to mention.

CTE Invalid column name

I am completely stuck on this one. I looked in the other questions, but could not find one that answered this (that I could understand, anyway). I have the following CTE in my query but MaxUserID is squiggled red in the 3 places it's used with an "invalid column name 'MaxUserID'" error. The column it should represent is an int, if that helps. Any advice?
I'm using SQL Server 2008.
;with TotalCount(TotalCount,MaxUserID)
as
(
Select ISNULL(count(distinct uCPR.HeaderID), 0) as TotalCount, MaxUserID
from ClientFeedback.dbo.UnitCountCPR uCPR
where
uCPR.DHDate between #StartDate and #EndDateMod
and uCPR.TargetID in (#StatusID)
and uCPR.UserID = MaxUserID
and uCPR.DTStamp between #StartDate and #EndDateMod
and uCPR.ClientID in (#ClientID)
group by MaxUserID
)
Refresh your intellisense cache, and then use your table alias to pick the coluumns. Also, count will never be null, your IsNull isn't doing you any good. And is there a reason you are using in instead of equals?

Why does the sql select statement fail?

In the scenario below, the final select from the Combine view fails, any ideas why?
The Subset table does not have a row that corresponds to the one in MasterCodes that wouldn't cast to an integer value.
CREATE TABLE MasterCodes (
ID INT
, Code VARCHAR(10) )
GO
CREATE TABLE Subset (
ID INT )
GO
CREATE VIEW Combine AS
SELECT S.ID
, M.Code
, CAST(M.Code AS INT) IntCode
FROM Subset S
INNER JOIN MasterCodes M ON M.ID = S.ID
GO
INSERT MasterCodes (ID, Code) VALUES (1, '1')
INSERT MasterCodes (ID, Code) VALUES (2, '2')
INSERT MasterCodes (ID, Code) VALUES (3, 'three')
INSERT MasterCodes (ID, Code) VALUES (4, '4')
INSERT Subset (ID) VALUES (1)
INSERT Subset (ID) VALUES (2)
INSERT Subset (ID) VALUES (4)
SELECT * FROM Combine -- 3 rows returned
SELECT * FROM Combine WHERE Code = '2' -- 1 row returned
SELECT * FROM Combine WHERE Code = '3' -- 0 rows returned
SELECT * FROM Combine WHERE IntCode = 2 -- fails, error msg is
Msg 245, Level 16, State 1, Line 15
Conversion failed when converting the varchar value 'three' to data type int.
Environment is Sql2k5 Standard (64-bit) ON Win2k3 Server R2
Probably because in some row, M.Code contains the literal word "three", and the view is trying to cast a non-numeric looking word into an int (you can probably cast "3" into an int, but not "puppy" or "three", etc.).
Edit: Added a comment, but worthwhile to add it here. SQL Server is going to try and execute and join the as efficiently as possible, and it's going try and apply the where clause apparently even before joining.
This makes sense if you consider that VIEWs nowadays work almost fully like a real table. It has to do something SIMILAR to this; otherwise, it will join everything and return all values BEFORE being filtered out.
Hideously expensive.
What I'm not sure about is if the execution plan will show this level of detail.
You're inserting the string "three" into MasterCodes.Code, and your view is attempting to cast this value to an integer. This SQL should give the same error:
select cast("three" as int)
Solution? Replace "three" with "3" in your insert statement.
Because when you try to use IntCode in your condition logic the view try's to cast "three" as an int.
Use isnumeric() with a case statement to create the view
case when isnumeric(field) then
cast(field as int)
else
null
end AS IntCode
If your result set is accurate:
"SELECT * FROM Combine WHERE Code = '2' -- 1 row returned
SELECT * FROM Combine WHERE Code = '3' -- 0 rows returned
SELECT * FROM Combine WHERE IntCode = 2 -- fails, error msg isMsg 245, Level 16, State 1, Line 15Conversion failed when converting the varchar value 'three' to data type int."
then the only time it fails is when you try to compare against IntCode field, it almost seems like it is failing when it tries to put the non-numeric value on the left side of the "IntCode = 2" comparison, because this is the only time it will need to pick up every single value in the code field.
Hope that helps!