I want to implement following logic in my SQL query:
If some date is not set or it's year is 1970 then select real_date flag to null AND change date to current data, else - real_date is true and no need to change date.
real_date is not an actual field in the table but a flag I need to set up.
I can easily do this using 2 lines in my SELECT section:
, IF (actualDate is NULL OR YEAR(actualDate) = 1970, CURRENT_TIMESTAMP(), actualDate) as actual_date
, IF (actualDate is NULL OR YEAR(actualDate) = 1970, null, true) as real_date
And the question - is it the only way to do it? Don't really like the fact that I had to copy condition again. Can somehow the second select be moved to the first one?
Update: I need to select both actualDate (it will be either fixed to current time version or the actual stored value) and real_date (which will be true or false/null). Maybe I am missing something, but how could COALESCE function help here?
Update 2: Thanks again everyone. Learnt other ways to write it but all of them have check in 2 places. My idea was to have logical condition in only one place but doesn't seem to be possible.
Your variant is pretty and comfortable for supporting code in future, do not change anything.
You can use the COALESCE function.
Returns the first non-NULL value in the list, or NULL if there are no non-NULL values.
You can also use CASE WHEN ... THEN ... END (with multiple WHEN) instead of the two IF
`
Check this here : http://dev.mysql.com/doc/refman/5.0/en/control-flow-functions.html
But the COALESCE function can do the job for this case.
I might try something like this instead:
…
, COALESCE(NULLIF(actualDate, '1970-01-01'), CURRENT_TIMESTAMP()) AS actual_date
, NULLIF(actualDate <> '1970-01-01', false) AS is_real_date
…
Or maybe the second NULLIF() is not really necessary? Consider this:
…
, COALESCE(NULLIF(actualDate, '1970-01-01'), CURRENT_TIMESTAMP()) AS actual_date
, (actualDate <> '1970-01-01') AS is_real_date
…
The second column could then be true, false or NULL, and you could treat NULL same as false.
This is the most compact form I can think of using COALESCE and without repeating any comparison:
select id, type, real_date, if(real_date, actual_date, now()) as actual_date
from (
select *, coalesce(year(actual_date), 1970) != 1970 as real_date from table1
) as subResult
Working example
Related
I have the following query:
SELECT
NULLIF(MAX(t.date),'2019-01-15') AS ended
FROM
totals t
This query correctly outputs a date:
> 2019-01-01
But if I reference this query inside of a subquery like so:
SELECT * FROM
(SELECT
NULLIF(MAX(t.date),'2019-01-15') AS ended
FROM
totals t) AS a
This version incorrectly produces a truncated result:
> 201
Can someone help me to understand this behavior and how best to work around it?
Additional Notes:
I am running MySQL version: "5.7.25 MySQL Community Server"
For anyone wanting to test this out, here is an example of a simple test table that is affected by this problem:
CREATE TABLE `totals` (
`date` date NOT NULL,
`value` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`date`)
);
INSERT INTO `totals` VALUES ('2018-01-01',2000000),('2019-01-01',3000000);
The issue here appears to be a subtle converting/casting issue happening within NULLIF. First, here is a version of your query which does in fact work as expected:
SELECT *
FROM
(
SELECT NULLIF(MAX(t.date), STR_TO_DATE('2019-01-15', '%Y-%m-%d') AS ended
FROM totals t
) AS a
What is happening with your current query is that MySQL is converting the call to MAX(t.date) to text, to match the text literal '2015-01-15'. By ensuring that both arguments to NULLIF are date type, you get the behavior you want.
As for why we are seeing 201 as the string result from the call to NULLIF, I don't have an explanation. But, I can cite the documentation for NULLIF here:
Returns NULL if expr1 = expr2 is true, otherwise returns expr1. This is the same as CASE WHEN expr1 = expr2 THEN NULL ELSE expr1 END.
It is a general rule in SQL that both the if and else branches of a CASE expression should always have the same type. Actually, if we violate this rule, in most databases the CASE expression won't even compile. Applying this to NULLIF means that we should always make sure that both arguments have the same type. Breaking from this rule might run on MySQL (similar to doing a non ANSI compliant GROUP BY with full mode turned off), but it is not something we should choose if we can avoid it.
My guess is that there's implicit datatype conversions happening.
As a workaround, I would take the return from the NULLIF function and convert/cast it back to DATE datatype. Simplest would be to wrap it in DATE() function.
SELECT
DATE( NULLIF(MAX(t.date),'2019-01-15') ) AS ended
^^^^^ ^
We could also try converting the string literal, convert that to a DATE, and see if that fixes the problem:
SELECT
NULLIF(MAX(t.date), DATE('2019-01-15') ) AS ended
^^^^^ ^
Or we can do both:
SELECT
DATE( NULLIF(MAX(t.date), DATE('2019-01-15') ) ) AS ended
^^^^^ ^
^^^^^ ^
There are other expressions we can use for the datatype conversion such as CAST(), CONVERT(), or STR_TO_DATE().
Or we could just use the simple + INTERVAL 0 DAY trick. e.g.
SELECT
NULLIF(MAX(t.date),'2019-01-15' + INTERVAL 0 DAY ) + INTERVAL 0 DAY AS ended
^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
I currently am using this query to select some data:
SELECT DISTINCT a.code AS code, name, max(scen.Is3D) AS Is3D FROM locations LEFT JOIN .... The scen table has columns Is3D and Date. I only want to select the max of items where the date IS NOT NULL. I tried max(scen.Is3D WHERE scen.Date IS NOT NULL), but that didn't work. I cannot change anything after the FROM in my query, so I need that filtering to be done in the MAX, if possible. I am using MySQL 5.7.
You can use:
MAX(CASE WHEN scen.date IS NOT NULL THEN scen.Is3D END) AS Is3D
The CASE expression returns NULL when none of the WHEN conditions is met, but MAX() ignores null values, so this will just return the max of the Is3D columns in the selected rows.
So if we can't change anything after the FROM, then we cannot get a perfect solution here. Since you are SELECTing out the NULL values. One thing that we can try if we can only modify the final output is this.
SELECT MAX(ISNULL(scen.Date,0))...
This will replace all the NULLs with 0, but it would help to know exactly what you are trying to do. Why are you so convinced that the query itself cannot be modified in any way?
The other solution would be to put the whole query in another wrapper.
That would look like:
SELECT *
FROM (
[your whole query here]
) AS inner
WHERE inner.Date IS NOT NULL
I currently have two queries, one which displays all data (regardless of time), another which considers a time frame. I'm trying to condense these together and was wondering if mysql has something for dateTimes which acts as an "allTime" variable, that is when plugged into:
select count(*) from table where (color = "red") and (dateMade between "allTime" and "allTime");
would display the row count of where color is red, for all rows in the table. A solution I thought of was to just use a minimum date of 0000-01-01 00:00:00 and some large end bound date, but if there already exists something to solve this I thought I might as well use that.
EDIT: This would be used on more than just the example query above, with most being much longer and more complex. The purpose is to combine this with node to make functions simpler and not have to consider a special case of all data, rather just use the time frame query and return all data based on the bounds given as arguments
Fill the variables #from and/or #to with NULL, if you don't want to restrict the data. Use IFNULL to check if the restriction is to be applied or not.
select count(*)
from table
where color = #color
and datemade between ifnull(#from, datemade) and ifnull(#to, datemade);
For cases when you don't need the date range you could make part of the where clause a true-ish noop:
select ...
from tablename
where date_created between $start and $end or $start = $end
Then, if you need the unbounded query, just pass the same $start and $end to it.
Don't pass the data twice unless you really have to, you could use a "conditional aggregation" instead, e.g.
select
count(*) all_count
, count(case when dateMade between #start and #end then id end) period_count
from table
where (color = "red")
To get "all time" you simply do not filter by date range in the where clause.
Note too that COUNT() function increments for any non-null value it encounters, so for the case expression it should return a column that will always be present in a row such as the primary key.
An alternative is to use SUM() instead, like this
select
count(*) all_count
, sum(case when dateMade between #start and #end then 1 else 0 end) period_count
from table
where (color = "red")
How do you replace a NULL value in the select with an empty string?
It doesn't look very professional to output "NULL" values.
This is very unusual and based on my syntax I would expect it to work.
I'm hoping for an explanation why it doesn't.
select CASE prereq WHEN (prereq IS NULL) THEN " " ELSE prereq end from test;
Example of what the original table looks like, what I want, and what actually prints:
original wanted what actually prints
-------- ------ ---------------------
value1 value1
NULL NULL
value2 value2
NULL NULL
As you can see it does the opposite of what I want, hence I tried flipping the IS NULL to IS NOT NULL and of course that didn't fix it. I also tried swapping the position of when case, which did not work.
It seems the 3 solutions given below all do the task.
select if(prereq IS NULL ," ",prereq ) from test
select IFNULL(prereq,"") from test
select coalesce(prereq, '') from test
If you really must output every values including the NULL ones:
select IFNULL(prereq,"") from test
SELECT COALESCE(prereq, '') FROM test
Coalesce will return the first non-null argument passed to it from left to right. If all arguemnts are null, it'll return null, but we're forcing an empty string there, so no null values will be returned.
Also note that the COALESCE operator is supported in standard SQL. This is not the case of IFNULL. So it is a good practice to get use the former. Additionally, bear in mind that COALESCE supports more than 2 parameters and it will iterate over them until a non-null coincidence is found.
Try below ;
select if(prereq IS NULL ," ",prereq ) from test
Some of these built-in functions should work:
COALESCE(value,...)
Returns the first non-NULL value in the list, or NULL if there are no non-NULL values.
IS NULL
Tests whether a value is NULL.
IFNULL(expr1,expr2)
If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2.
select IFNULL(`prereq`,'') as ColumnName FROM test
this query is selecting "prereq" values and if any one of the values are null it show an empty string as you like
So, it shows all values but the NULL ones are showns in blank
The original form is nearly perfect, you just have to omit prereq after CASE:
SELECT
CASE
WHEN prereq IS NULL THEN ' '
ELSE prereq
END AS prereq
FROM test;
Try COALESCE. It returns the first non-NULL value.
SELECT COALESCE(`prereq`, ' ') FROM `test`
Try this, this should also get rid of those empty lines also:
SELECT prereq FROM test WHERE prereq IS NOT NULL;
I have nulls (NULL as default value) in a non-required field in a database. They do not cause the value "null" to show up in a web page. However, the value "null" is put in place when creating data via a web page input form. This was due to the JavaScript taking null and transcribing it to the string "null" when submitting the data via AJAX and jQuery. Make sure that this is not the base issue as simply doing the above is only a band-aid to the actual issue. I also implemented the above solution IFNULL(...) as a double measure. Thanks.
UPDATE your_table set your_field="" where your_field is null
We have a table that has either NULL or "Accepted" as values. My query returns about 250 rows.
If I add a where condition of -
AND Description = 'Accepted'
my 250 rows return in 2 seconds.
However, if I add a where condition of -
ISNULL(Description, '') = 'Accepted'
my 250 rows return in 47 seconds.
Has anyone encountered performance issues with using the ISNULL function? Unfortunately I am programatically limited to having to use ISNULL at this point.
When you include a field inside of a function, it changes how the optimizer has to run and forces it to ignore indexes.
see here: What makes a SQL statement sargable?
You can also bypass the functions entirely by using:
WHERE (Description = 'Accepted' OR Description IS NULL)
Using
ISNULL(Description, '') = 'Accepted'
in your where condition doesnt make any sense in this case. If the description is null, the original where clause of
AND Description = 'Accepted'
will still suffice.
You are basically comparing '' with 'Accepted' in every row where the description is null.
Please elaborate on what you are trying to accomplish with the query, i think you might be going in the wrong direction.
If you are trying to use this in the WHERE condition, use IS NULL, not ISNULL
SELECT field FROM table WHERE description is null
or the opposite
SELECT field FROM table WHERE NOT description is null