I have a simple query and am noticing when I write a sum statement, the resultant sum numbers do not match what I would expect. I think it is when evaluating a column with 'datetime' format. Unsure how to get around it. Having trouble searching the correct phrase to find the solution, but I expect it to be a common one. Maybe a cast or convert to DATE format, but those did result in answers which made sense.
SELECT X.X_ITEMID,
SUM(CASE WHEN X.ActivityDate < '11/29/2016' THEN X.Qty ELSE 0 END) AS SUM_X
FROM ERP_X X
WHERE X_ITEMID = 'abcdef'
GROUP BY X.X_ITEMID
When you compare date[time]s to character literals, the date is converted to a character (using the default date format), and then they are compared lexicographically, which is almost always a bad idea. Instead, you should explicitly convert your string literal to a datetime, and allow the database to compare them properly:
SELECT X.X_ITEMID,
SUM(CASE WHEN X.ActivityDate < CONVERT(datetime, '11/29/2016', 101)
THEN X.Qty
ELSE 0
END) AS SUM_X
FROM ERP_X X
WHERE X_ITEMID = 'abcdef'
GROUP BY X.X_ITEMID
Try bellow code, I hope it gives you correct result what you need.
SELECT X.X_ITEMID,
SUM(CASE WHEN convert(char(8),X.ActivityDate,112) < '20161129'
THEN X.Qty
ELSE 0
END) AS SUM_X FROM ERP_X X
WHERE X_ITEMID = 'abcdef'
GROUP BY X.X_ITEMID
Related
I am trying to clean my data prior to loading into PowerBI to create the visuals. I have created my query as:
CREATE VIEW Project2 AS (
Select
p.playerID as ID1,
p.birthYear,
p.birthMonth,
p.birthDay,
p.birthCity,
p.deathYear,
p.nameFirst,
p.nameLast,
p.nameGiven,
p.weight,
p.bats,
p.throws,
p.finalGame,
b.*
from dbo.People as p
LEFT JOIN dbo.Batting as b
ON p.playerID=b.playerID
and b.G >= 50
WHERE (p.finalGame is null or p.finalGame >= 2018));
This works great until attempting to load the view into PowerBI I get this error:
DataSource.Error: Microsoft SQL: Conversion failed when converting the varchar value '2015-08-23' to data type int.
Details:
DataSourceKind=SQL
DataSourcePath=laptop-o4rhi9q7;Baseball
Message=Conversion failed when converting the varchar value '2015-08-23' to data type int.
ErrorCode=-2146232060
Number=245
Class=16
I can't figure out where /how to utilize cast(p.finalGame as date) in correct syntax, any ideas?
You must use a proper date literal for this, which means it must be enclosed in quotes, and ideally use a non-ambiguous format
WHERE (p.finalGame is null or p.finalGame >= '20180101'));
If finalGame is actuall varchar you would need to convert it using an appropriate conversion type. For example
WHERE (p.finalGame is null or CONVERT(date, p.finalGame, 102) >= '20180101'));
That may not be the correct format number, the full list is here.
I urge you to change the column type to date in the first place.
If the data for p.finalGame = '2015-08-23', you'll need to cast that as a date in your WHERE clause.
CREATE VIEW Project2 AS (
Select
p.playerID as ID1,
p.birthYear,
p.birthMonth,
p.birthDay,
p.birthCity,
p.deathYear,
p.nameFirst,
p.nameLast,
p.nameGiven,
p.weight,
p.bats,
p.throws,
p.finalGame,
b.*
from dbo.People as p
LEFT JOIN dbo.Batting as b
ON p.playerID=b.playerID
and b.G >= 50
WHERE (p.finalGame is null or cast(p.finalGame as DATE) >= 2018));
Based on the error message, you date appears to be a string of the form 'yyyy-mm-dd', not a true date type or an integer year. I believe your fix is to quote your year. Either
WHERE (p.finalGame is null or p.finalGame >= '2018');
or
WHERE (p.finalGame is null or p.finalGame >= '2018-01-01');
If your year is a variable, convert it to a string with something like CONVERT(VARCHAR(10), #Year). (This assumes a 4 digit value.)
As already noted, a better approach (if you have the ability to change the schema) is to redefine finalGame as a true DATE or DATETIME type. Then you could compare p.finalGame >= '2018-01-01' or p.finalGame >= DATEFROMPARTS(#Year, 1, 1).
I am trying to first count characters in the columns and than dividing them to create a percentage ratio column. I get errors and not sure what is causing the issue.
[Report_Graded] is boolean
[Test_Status] is text with (4) options. "Accepted, Completed, Declined, Scheduled"
SELECT Name, Report_Graded, Test_Status,
SUM(CASE WHEN Report_Graded= 'Completed' THEN 1 ELSE 0 END) / SUM(CASE WHEN Test_Status= 'Declined'OR 'Scheduled' THEN 0 ELSE 1 END) * 100 AS "Ratio_Graded"
FROM [database]
WHERE Date_Test BETWEEN '01/01/2021' AND '06/30/2021'
GROUP BY Name
ORDER BY "Ratio_Graded" DESC
WHEN Test_Status= 'Declined'OR 'Scheduled'
is not syntactically correct. You must write
WHEN Test_Status= 'Declined'OR Test_Status= 'Scheduled'
To run the query.
Also beware of O divide. Add a COALESCE.
The query rewrited :
SELECT Name,
Report_Graded,
Test_Status,
SUM(CASE
WHEN Report_Graded = 'Completed'
THEN 1
ELSE 0
END) / COALESCE(SUM(CASE
WHEN Test_Status = 'Declined'
OR Test_Status = 'Scheduled'
THEN 0
ELSE 1
END) * 100 AS "Ratio_Graded"
FROM [database]
WHERE Date_Test BETWEEN '01/01/2021' AND '06/30/2021'
GROUP BY Name
ORDER BY "Ratio_Graded" DESC;
Also date/time format can vary from the session culture. It is why it is recommended to use the ISO SQL Standard :
AAAAMMJJ hh:mm:ss.nnn for DATETIME (call ISO short)
AAAA-MM-JJ hh:mm:ss.nnn for DATETIME2, DATETIMEOFFSET (call ISO
long)
AAAA-MM-JJ for DATE (call ISO long)
I want my SQL to display the overdue count when the condition is the status name showed closed on the exact due date then the count will be set as 1. For example, on the due date, the status name only became closed.
select
category, COUNT(overdue) as overdue2
from
(select
Category, due,
case when DATEDIFF(day, Due, SYSDATETIME()) = 0 then 1
else 0
end as overdue
from
FeedbackDetail
where
StatusName = 'Closed' and
FeedbackDatetime >= '2018-01-01') a
Group by
Category
My expected result is to display the count where the statusname is closed on the exact due date time.
Any idea on this?
The COUNT aggregate function counts existant (non-null) values, so it will count 0 as well as 1. Since you did not post the whole query and we have no idea what a1 is, the only solution that can be proposed is:
Use SUM instead of COUNT.
You can modify the query like given below for better performance and working.
DECLARE #currentDateTime DATETIME = GETDATE()
select
category, SUM(overdue) as overdue2
from
(select
Category,
case when DATEDIFF(day, Due, #currentDateTime) = 0 then 1
else 0
end as overdue
from
FeedbackDetail
where
StatusName = 'Closed' and
FeedbackDatetime >= '2018-01-01') a
Group by
Category
i'm trying to build a query in which I need to apply 2 different where clauses, depending on the value of Current Month. In this case, I need to show data from the last 2 years, only of the months before the current month:
Example 1:
Current Date is: 01-01-2017
Need to show data from:
01/2015; 02/2015; 03/2015; 04/2015; 05/2015; 06/2015;
07/2015; 08/2015; 09/2015; 10/2015; 11/2015; 12/2015;
01/2016; 02/2016; 03/2016; 04/2016; 05/2016; 06/2016;
07/2016; 08/2016; 09/2016; 10/2016; 11/2016; 12/2016.
Example 2:
Current Date is: 01-03-2017
Need to show data from: 01/2016; 02/2016; 01/2017; 02/2017.
So I built the following query:
SELECT *
FROM TABLE1
WHERE
CASE MONTH(GETDATE())
WHEN 1
THEN YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE())
ELSE YEAR(Data)>=YEAR(GETDATE())-1 and YEAR(data)<=YEAR(data) and MONTH(data)<MONTH(GETDATE())
END
I'm getting an error.
Can you please help me?
Thank you.
Your syntax is incorrect for sure. THEN is not a logical expression - it is supposed to return value. So you can't write logical expression in THEN/ELSE blocks as you have attempted to. Instead you might try something like:
WHERE
#date >= CASE WHEN a=b THEN '20150101' ELSE '20160202' END
Another thing is: conversions and functions in predicate are very bad for performance. When working with dates you might want to prepare filter predicate before the query when possible, e.g.:
declare
#date_begin date,
#date_end date
set #date_end = DATEADD(..., #arg_date)
set #date_begin = DATEADD(YEAR, -2, #date_end)
select ...
where date between #date_begin and #date_end
in your case it could be something like:
declare
#arg_date DATE = GETDATE(),
#date_begin DATE,
#date_end DATE,
#max_month INT
set #max_month = MONTH(#date)
if #max_month = 1
begin
set #date_end = DATEADD(dd, 1-DATEPART(dy, #arg_date), #arg_date) /* first day of year */
set #date_begin = dateadd(YY, -2, #date_end)
end
else
begin
set #date_end = #arg_date
set #date_begin = dateadd(YY, -1, DATEADD(dd, 1-DATEPART(dy, #date_end), #date_end)) /* first day of year_begin */
end
SELECT *
FROM TABLE1 t
WHERE t.date >= #date_begin and t.date < #date_end
AND (#max_month = 1 OR MONTH(t.date) < #max_month)
another (a better) way is to prepare #periods table variable, put each (date_begin, date_end) pair you need into it and join with TABLE1 - you'll get rid of all function calls from within WHERE clause.
You should realize: you know exactly which periods of each year you need in the result set. There is nothing to compute from stored TABLE1->date column. Just filter it with precomputed date intervals. Don't convert or modify date column - it is already ready to use. Merely apply appropriate filters. MONTH(date) <= 3 is date <= 20170331. Don't torture left part - prepare appropriate right part of such predicates.
The easiest way would be something like:
SELECT *
FROM TABLE1
WHERE
(YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE()) AND MONTH(GETDATE()) = 1)
OR (YEAR(Data)>=YEAR(GETDATE())-1 and MONTH(data)<MONTH(GETDATE()) and MONTH(GETDATE()) <> 1)
(Note I removed the superfluous and YEAR(data)<=YEAR(data).).
Personally I prefer (and I think it's generally advised) AND/OR logic to a CASE in a WHERE clause.
The error with your CASE statement is caused by the fact that CASE returns an atomic value. It cannot be used in the same way as if in procedural languages.
You can't swap in additional statements to your where clause using case statements. Instead, you need to resolve the case to an equality:
select *
from Table1
where case month(getdate()) -- You want to avoid using functions on fields in your WHERE claises, as this can reduce performance.
when 1 then case when Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
then 1 -- Data rows the meet the criteria will return 1.
else 0 -- Data rows that do not will return 0.
end
else case when (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
then 1
else 0
end
end = 1 -- Then limit the results to only those rows that returned a 1.
In your specific instance however, this can be simplified to a standard or:
select *
from Table1
where (month(getdate()) = 1
and Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
)
or (month(getdate()) <> 1
and (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
)
Note the use of brackets above to separate out the logical tests. Where a Data row meets either one of those criteria it will be returned in your query.
I have a column which I want to convert to decimal so I can then use it to compare in my where clause. I want to make sure all values from the column are greater or equal to 1.3. I converted the column successfully in the select statement but when attempting to do the same convert in the where clause I get the following error:
Arithmetic overflow error converting varchar to data type numeric.
I am using SQL Server 2008.
SELECT ID,
CASE
WHEN ISNUMERIC(USER_3) = 1
THEN Convert(varchar(50), CONVERT(decimal(14,2), USER_3))
END AS KG_M
FROM PART
WHERE USER_3 IS NOT NULL
AND CASE
WHEN ISNUMERIC(USER_3) = 1
THEN Convert(varchar(50), CONVERT(decimal(14,2), USER_3))
END >= 1.3
Sure, why not? Here's a self-contained example:
select a.ID
, b.KG_M
from (values
(1, N'12345678')
, (2, N'ABCDEFGH')
) as a (ID, USER_3)
cross apply (values(
case IsNumeric(a.USER_3)
when 1 then Convert(varchar(50), Convert(decimal(14, 2), a.USER_3))
else a.USER_3
end
)) as b (KG_M)
where b.KG_M >= '1.3';
We simply use the APPLY operator to contain our calculation for reuse later.
You need to choose one way to convert. I would use the native type for comparison, decimal.
SELECT * FROM
(
SELECT ID, KG_M=CAST(USER_3 AS decimal(14,2))
FROM PART
WHERE
ISNUMERIC(USER_3) = 1
)AS X
WHERE
X.KG_M >= 1.3
Allow strings that are not numbers in outoput
SELECT * FROM
(
SELECT
ID,
USER_3_AsDecimal=CASE WHEN ISNUMERIC(USER_3) THEN CAST(USER_3 AS decimal(14,2)) ELSE NULL END,
USER_3
FROM PART
WHERE
NOT USER_3 IS NULL
)AS X
WHERE
X.USER_3_AsDecimal IS NULL
OR
X.USER_3_AsDecimal >= 1.3
The problem was a syntax error, the case in the where clause was a success the entire time.
"you should use >= '1.3' since you are converting to varchar" credit to #Lamak in comments