Case Function in Oracle - wrong return - sql

I have an table with these fields:
id,
quarter,
description,
id_school.
These table have this data:
ID | quarter | description
12 A 1 YEAR
12 S 1 Year
12 A DONE
I want to get the serie_school based on the below query:
select NVL(
case
WHEN quarter = 'S' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%YEAR' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1
WHEN quarter = 'A' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%QUARTER' OR
UPPER(description ) LIKE '%STEP' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1
WHEN quarter = 'T' THEN -1
ELSE -1
end, -1) nvl_return from test
The return of this on my query is:
ID | quarter | description | nvl_return
12 A 1 YEAR 1
12 S 1 Year 1 *- (this column has the wrong answer)*
12 A DONE -1
The answer of line 2 is wrong, because the QUARTER field is 'S' and the description field have 'year', so it needs to be -1, but in Oracle is returning 1.
Is there anyone who can help me with this?
Thanks in advance

Alexandre,
Examine your statement, where I have added "<======" to point out the various possible amounts for this column.
select NVL(
case
WHEN quarter = 'S' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%YEAR' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1<=======
WHEN quarter = 'A' AND
(UPPER(description ) LIKE 'DONE' OR
UPPER(description ) LIKE '%QUARTER' OR
UPPER(description ) LIKE '%STEP' OR
UPPER(description ) LIKE 'LANGUAGE') THEN -1<======
WHEN quarter = 'T' THEN -1 <======
ELSE -1 <=======
end, -1 <-this will never happen) nvl_return from test
You return "-1" in every single case. "-1" is the only value returned, by the only column returned in your query.
So when you say the results are:
ID | quarter | description | nvl_return
12 A 1 YEAR 1
12 S 1 Year 1 *- (this column has the wrong answer)*
12 A DONE -1
Unfortunately, your query cannot produce the results you've stated.
Try creating a SQL fiddle, to explain the issue. SQL Fiddle

Related

SQL Melt after Mapping Columns

I have the following working query:
SELECT
postgres."week" AS "week",
CASE
WHEN (postgres."spent_money_office") = 1 THEN (postgres."weight")
WHEN (postgres."spent_time_office") = 1 THEN (postgres."weight")
WHEN (postgres."spent_other_office") = 1 THEN (postgres."weight")
ELSE 0
END AS "office_leisure",
CASE
WHEN (postgres."spent_money_home") = 1 THEN (postgres."weight")
WHEN (postgres."spent_time_home") = 1 THEN (postgres."weight")
WHEN (postgres."spent_other_home") = 1 THEN (postgres."weight")
ELSE 0
END AS "home_leisure",
CASE
WHEN (postgres."spent_money_vacation") = 1 THEN (postgres."weight")
WHEN (postgres."spent_time_vacation") = 1 THEN (postgres."weight")
WHEN (postgres."spent_other_vacation") = 1 THEN (postgres."weight")
ELSE 0
END AS "vacation_leisure"
FROM
"lake"."postgres" AS "postgres"
GROUP BY
1,
2,
3,
4
ORDER BY
1
Which gives 4 columns: week, office_leisure, home_leisure, and vacation_leisure as shown here:
I would like to "melt" the leisure columns into one called leisure_type taking on 3 values, either office/home/vacation and another column called weight. How can this be done using the SQL code query above?
Something like:
week
leisure_type
weight
week 1
office
1.78
week 1
home
1.78
week 1
vacation
1.78
week 1
office
0
...
...
...
You can apply a union over the selection of the week, leisure type and leisure value, for each leisure type you have (so three times):
SELECT week,
'office_leisure' AS leisure_type,
office_leisure AS weight
FROM tab
UNION
SELECT week,
'home_leisure' AS leisure_type,
home_leisure AS weight
FROM tab
UNION
SELECT week,
'vacation_leisure' AS leisure_type,
vacation_leisure AS weight
FROM tab
You can find a fiddle here.

order date for values with unknown day and month

I'm developing a website with events from the past. For some events the whole date is known, for some the month of the year and for some only the year. Table event looks like:
id
content
date -> YYYY-MM-DD (date data type)
year -> true, if only year is known, otherwise false
monthandyear -> true if only year and month is known, otherwise false
I would like to list the contents of the table in the following order:
descending by date
if only the month and year are known (monthandyear is set to
true), the content should be listed as the last of that month
if only the year is known (year is set to true), the content should be listed as the last of that year
What is the best solution to this problem?
I'm using DQL:
SELECT p FROM App:Event p ORDER BY p.date DESC ...
You can use conditional logic. Assuming that date is a string:
order by left(date, 4),
(case when not year and not yearmonth then 1 else 2 end),
left(date, 7),
(case when not yearmonth then 1 else 2 end)
date
Edit:
If date is of type date, then:
order by year(date),
(case when not year and not yearmonth then 1 else 2 end),
month(date),
(case when not yearmonth then 1 else 2 end)
date
I solved the problem using Gordon's answer. In DQL it looks like:
$this->getEntityManager()
->createQuery(
'SELECT '
. 'p, '
. '(CASE WHEN (p.year = :yes) THEN 1 ELSE 2 END) as HIDDEN ORD1, '
. '(CASE WHEN (p.monthandyear = :yes) THEN 1 ELSE 2 END) as HIDDEN ORD2 '
. 'FROM App:Event p '
. 'ORDER BY SUBSTRING(p.dateandtime, 1, 4) DESC, ORD1 DESC, SUBSTRING(p.dateandtime, 1, 7) DESC, ORD2 DESC, p.dateandtime DESC'
)
->setParameters(array("no" => false, "yes" => true))
->getResult();

Using COUNT CASE WHEN MONTH Statement in MariaDB 10.2.15

I created a query to calculate the Amount of Id in a table using COUNT, CASE, WHEN and MONTH ..
Code:
SELECT
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN LogsFormatted.Id ELSE 0 END ) AS '1',
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 2 THEN LogsFormatted.Id ELSE 0 END ) AS '2'
FROM
HrAttLogsFormatted AS LogsFormatted
WHERE
LogsFormatted.DateIn BETWEEN '2019-01-01' AND '2019-02-31'
AND LogsFormatted.Late != ''
Output :
| 1 | 2 |
| 1378 | 1378 |
The output I want to make is to calculate the Id in each month, namely Month 1 and Month 2
| 1 | 2 |
| 792 | 586 |
The data above is a fact
Using the above query instead adds up between the results of calculating month 1 and month 2
You should be counting NULL when the criteria in your CASE expression does not match. Also, I prefer counting 1 unless you really want to the count the Ids themselves. This version should work:
SELECT
COUNT(CASE WHEN MONTH(lf.DateIn) = 1 THEN 1 END) AS '1',
COUNT(CASE WHEN MONTH(lf.DateIn) = 2 THEN 1 END) AS '2'
FROM HrAttLogsFormatted AS lf
WHERE
lf.DateIn BETWEEN '2019-01-01' AND '2019-02-31' AND
lf.Late != '';
Note carefully that the current counts you are seeing sum up to the individual counts, that is:
1378 = 792 + 586
The reason for this is the the COUNT function "counts" any non NULL value as 1, and any NULL value as zero. Your current CASE expression will always count 1, for every record in the table.
remove else part from case when expression - if you use else with 0 then count takes that also in consideration which gives u actually wrong ouput
SELECT
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 1 THEN LogsFormatted.Id END ) AS '1',
COUNT(CASE WHEN MONTH(LogsFormatted.DateIn) = 2 THEN LogsFormatted.Id END ) AS '2'
FROM
HrAttLogsFormatted AS LogsFormatted
WHERE
LogsFormatted.DateIn BETWEEN '2019-01-01' AND '2019-02-31'
AND LogsFormatted.Late != ''
If you are using MariaDB, I would just use SUM() with a boolean:
SELECT SUM( MONTH(lf.DateIn) = 1 ) as month_1,
SUM( MONTH(lf.DateIn) = 2 ) as month_2
FROM HrAttLogsFormatted lf
WHERE lf.DateIn >= '2019-01-01' AND
lf.DateIn < '2020-01-01' AND
lf.Late <> '';
This assumes that the id that you are counting is never NULL (a reasonable assumption for an id).
Note other changes:
The column aliases do not need to be escaped. Don't use non-standard names, unless you need them -- for some reason -- for downstream processing.
This uses a shorter table alias, so the query is easier to write and to read.
The date comparisons use inequalities, so this works both for dates and datetimes.
<> is the standard SQL comparison operator for inequality.

Getting derived column from database | SQL query | DB2 SystemDate

My table is PRODUCTINFO:
Column name : product_name | launch_date
Sample Date : product1 2017-01-20
I need a SQL query to decide if the product is newly launched or not.
Business rule :
if (launch_date - currentdate < 0 && currentdate - launch_date < 90)
newly_launched = 'YES'
else
newly_launched = 'NO'
where currentdate is today's date.
SQL query I am witting is like :
SELECT launch_date, X as newly_launched
FROM PRODUCTINFO
WHERE product_name = 'product1'
I am not able to figure out correct replacement of 'X' in my query for desired result.
Problem I am facing is using currentdate and if else block in my query
Please help.
One way to get currentdate in DB2 using following query :
SELECT VARCHAR_FORMAT(CURRENT TIMESTAMP, 'YYYYMMDD')
FROM SYSIBM.SYSDUMMY1
Still not sure how to use this with my if else scenario.
Answer here don't solve my problem as this scenario is if-else based.
use TIMESTAMPDIFF with 16 as first parameter for count number days between 2 timestamps, for more detail look here
try this
select launch_date,
case when TIMESTAMPDIFF( 16, cast(cast(launch_date as timestamp) - current timestamp as char(22))) >0 and
TIMESTAMPDIFF( 16, cast(cast(launch_date as timestamp) - current timestamp as char(22))) <90 then 'YES' else 'NO' end newly
from PRODUCTINFO
WHERE product_name = 'product1'
simply use datediff function in a case syntax, I hope it could help!
SELECT launch_date,
case
when DATEDIFF(day,launch_date,getdate()) BETWEEN 0 AND 90 then 'YES'
else 'NO'
end AS newly_launched
FROM PRODUCTINFO
WHERE product_name = 'product1'
in case you want to do it for all your records:
SELECT launch_date,
case
when DATEDIFF(day,launch_date,getdate()) BETWEEN 0 AND 90 then 'YES'
else 'NO'
end AS newly_launched
FROM PRODUCTINFO

Multiple Queries in different table

(Also posted here.)
So I have two tables, one is invalid table and the other is valid table.
valid table:
id
status
date
invalid table:
id
status
date
I have to produce a report with this output:
date on-time late total valid invalid1 invalid2 total rate
--------- ------- ---- ----- ----- -------- -------- ----- ----
9/10/2011 4 10 14 3 3 3 6
date: common fields on the 2 tables, field to group by, how many records on that day has
on-time: count of all the id on the valid table
late: count of all the records(id) on the invalid table
total: total of on-time and late
valid: count of id on the valid table with the "valid" status
invalid1: count of id on the invalid table with "invalid1" status
invalid2: count of id on the invalid table with "invalid2" status
total: total of valid, invalid1, invalid2
rate: average of totals
It's basically multiple queries with different table. How can I achieve it?
Someting like this?
SELECT
*,
(result.total + result._total) / 2 AS rate
FROM (
SELECT
date,
SUM(CASE WHEN data.valid = 1 THEN 1 ELSE 0 END) AS ontime,
SUM(CASE WHEN data.valid = 0 THEN 1 ELSE 0 END) AS late,
COUNT(*) AS total,
SUM(CASE WHEN data.valid = 1 AND data.status = 'valid' THEN 1 ELSE 0 END) AS valid,
SUM(CASE WHEN data.valid = 0 AND data.status = 'invalid1' THEN 1 ELSE 0 END) AS invalid1,
SUM(CASE WHEN data.valid = 0 AND data.status = 'invalid2' THEN 1 ELSE 0 END) AS invalid2,
SUM(CASE WHEN data.status IN ('valid', 'invalid', 'invalid2') THEN 1 ELSE 0 END) AS _total
FROM (
SELECT
date,
status,
valid = 1
FROM
Valid
UNION ALL
SELECT
date,
status,
valid = 0
FROM
InValid ) AS data
GROUP BY
date) AS result
SELECT date, ontime, late, ontime+late total, valid, invalid1, invalid2, valid+invalid1+invalid2 total
FROM
(SELECT date,
COUNT(*) late,
COUNT(IIF(status = 'invalid1', 1, NULL)) invalid1,
COUNT(IIF(status = 'invalid2', 1, NULL)) invalid2,
FROM invalid
GROUP BY date
) JOIN (
SELECT date,
COUNT(*) ontime,
COUNT(IIF(status = 'valud', 1, NULL)) valid,
FROM valid
GROUP BY date
) USING (date)
First of all, it seems that you are holding exactly the same information in 2 tables - I would recommend merging those tables together and add an additional boolean column called valid to hold the info related to validity of the record.
The query on your existent DB structure might look something like this:
SELECT unioned.* FROM (
( SELECT v.date AS date, v.status AS status, v.id AS id, COUNT(id) AS valid, 0 AS invalid1, 0 AS invalid2 FROM valid v GROUP BY v.date)
UNION
( SELECT i1.date AS date, i1.status AS status, i1.id AS id, 0 AS valid, COUNT(i1.id) AS invalid1, 0 AS invalid2 FROM invalid1 i1 GROUP BY i1.date)
UNION
( SELECT i2.date AS date, i2.status AS status, i2.id AS id, 0 AS valid, 0 AS invalid1, COUNT(i.id) AS invalid2 FROM invalid1 i1 GROUP BY i1.date)
) AS unioned GROUP BY unioned.date