Here is the query:
SELECT t1.id,
(
IF t1.sms_phone IS NOT NULL THEN t1.sms_phone
ELSE IF t1.insuringPhone IS NOT NULL THEN t1.insuringPhone
ELSE IF t1.person_id IS NOT NULL THEN (SELECT person_contact.value FROM person_contact where person_contact.person_id = t1.person_id AND person_contact.contact_id = 2 ORDER BY person_contact.last_change DESC LIMIT 1) END IF) as phone
FROM test_table as t1
WHERE id = 187842
Also tried with CASE without success:
SELECT t1.id,
(
CASE
WHEN t1.sms_phone IS NOT NULL THEN t1.sms_phone
WHEN t1.insuringPhone IS NOT NULL THEN t1.insuringPhone
WHEN t1.person_id IS NOT NULL THEN (SELECT person_contact.value FROM person_contact where person_contact.person_id = t1.person_id AND person_contact.contact_id = 2 ORDER BY person_contact.last_change DESC LIMIT 1)
) as phone
FROM test_table as t1
WHERE id = 187842
Use COALESCE which returns the first non-null argument.
SELECT t1.id, COALESCE(t1.sms_phone, t1.insuringPhone, ...)
For person_id an other solution seems better: maybe if a limited COALESCE returns NULL,
query further.
Related
I wanted to modify the query I wrote using the IN operator to using the Exists operator. However, the problem is that I have union all in my subquery. As I do not have much experience with using exists in such scenario I was looking for some help.
This was the initial query:
select APP_NUM,SBM_INDV_ID
from T1004_APP_INDV indv
where INDV.APP_NUM = 'T23952717'
and INDV.SBM_INDV_ID in
(select EMPL.INDV_ID
from DC_EMPLOYMENT EMPL
where EMPL.LOST_EMPLOYMENT_SW = 'N'
and EMPL.TERMINATION_DT is null
and (EMPL.EFF_END_DT is null
or EMPL.EFF_END_DT >= TRUNC(SYSDATE))
union all
select UNEARN.INDV_ID
from DC_UNEARNED_INCOME UNEARN
where UNEARN.EFF_END_DT is null
or UNEARN.EFF_END_DT >= TRUNC(SYSDATE)
union all
select RBINC.INDV_ID
from DC_ROOM_BOARD_INCOME RBINC
where RBINC.EFF_END_DT is null
or RBINC.EFF_END_DT >= TRUNC(SYSDATE)
union all
select SELFEMP.INDV_ID
from DC_SELF_EMP_INCOME SELFEMP
where SELFEMP.EFF_END_DT is null
or SELFEMP.EFF_END_DT >= TRUNC(SYSDATE)
);
I tried changing the initial query as follows:
select APP_NUM,SBM_INDV_ID
from T1004_APP_INDV APPINDV
where APPINDV.APP_NUM = 'T23952717'
and exists
(select EMPL.INDV_ID
from DC_EMPLOYMENT EMPL
where APPINDV.SBM_INDV_ID = EMPL.INDV_ID
and EMPL.LOST_EMPLOYMENT_SW = 'N'
and EMPL.TERMINATION_DT is null
and (EMPL.EFF_END_DT is null
or EMPL.EFF_END_DT >= TRUNC(SYSDATE))
union all
select UNEARN.INDV_ID
from DC_UNEARNED_INCOME UNEARN
where APPINDV.SBM_INDV_ID = UNEARN.INDV_ID
and UNEARN.EFF_END_DT is null
or UNEARN.EFF_END_DT >= TRUNC(SYSDATE)
union all
select RBINC.INDV_ID
from DC_ROOM_BOARD_INCOME RBINC
where APPINDV.SBM_INDV_ID = RBINC.INDV_ID
and RBINC.EFF_END_DT is null
or RBINC.EFF_END_DT >= TRUNC(SYSDATE)
union all
select SELFEMP.INDV_ID
from DC_SELF_EMP_INCOME SELFEMP
where APPINDV.SBM_INDV_ID = SELFEMP.INDV_ID
and SELFEMP.EFF_END_DT is null
or SELFEMP.EFF_END_DT >= TRUNC(SYSDATE));
Is the second query correct? Does it produce the same result as the previous query?
Thanks
I think splitting this into separate queries makes it simpler for the optimizer:
where APPINDV.APP_NUM = 'T23952717' and
( exists (select 1
from DC_EMPLOYMENT EMPL
where APPINDV.SBM_INDV_ID = EMPL.INDV_ID and
EMPL.LOST_EMPLOYMENT_SW = 'N' and
EMPL.TERMINATION_DT is null and
(EMPL.EFF_END_DT is null or EMPL.EFF_END_DT >= TRUNC(SYSDATE)
) or
exists (select 1
from DC_UNEARNED_INCOME UNEARN
where APPINDV.SBM_INDV_ID = UNEARN.INDV_ID and
( UNEARN.EFF_END_DT is null and or UNEARN.EFF_END_DT >= TRUNC(SYSDATE) )
) or
. . .
)
Having this table with 3 columns:
id name flag
1 A 1
2 A 0
3 A 0
4 B 0
5 B 0
6 C 0
7 D 1
I want to select all groups that does not have flag = 1
Expected results:
name
B
C
this is not working because ( correctly ) include all groups that has at list one
SELECT name
FROM test
WHERE flag <> '1'
GROUP BY name
Aggregation is one way to do this, but you need a HAVING clause to assert that a matching group has no flag = 1:
SELECT name
FROM test
GROUP BY name
HAVING SUM(flag = 1) = 0;
Demo
You could also use a subquery with exists logic:
SELECT t1.name
FROM test t1
WHERE NOT EXISTS (SELECT 1 FROM test t2 WHERE t2.name = t1.name AND t2.flag = 1);
You can use below query to get the desired output.
SELECT `name`
FROM `test`
WHERE `name` <> ALL ( SELECT `name` FROM `test` WHERE `flag` = 1 )
GROUP BY `name`;
Just use SELECT DISTINCT:
SELECT DISTINCT `name`
FROM `test`
WHERE `name` NOT IN (SELECT DISTINCT `name` FROM `test` WHERE `flag` = 1);
I have this two MySQL statements from same table :
SELECT
`table1`.`product_id` As product_id,
COUNT(DISTINCT(table1.user_id)) AS NonebuyersNumber
FROM table1
WHERE status = 1 AND `ispaid` != 2
GROUP BY `table1`.`product_id`
The second statement is :
SELECT l
`table1`.`product_id` As product_id,
COUNT(DISTINCT(table1.user_id)) AS BuyersNumber
FROM table1
WHERE `ispaid` = 1
GROUP BY `table1`.`product_id`
The result that I want is a table like this one :
I tried to use Union but doesn't work because I have two different columns
Any idea how I can get this 3rd table?
Use conditional aggregation:
SELECT
product_id,
COUNT(DISTINCT CASE WHEN status = 1 AND ispaid != 2
THEN user_id ELSE NULL END) AS NonebuyersNumber
COUNT(DISTINCT CASE WHEN ispaid = 1 THEN user_id ELSE NULL END) AS BuyersNumber
FROM table1
WHERE
(status = 1 AND ispaid != 2) OR
ispaid = 1
GROUP BY
product_id;
This should work because both of your queries aggregate over the product_id and the only differences are the WHERE clauses. We can combine the records from both queries and then use CASE expressions to target records intended for each original query.
SELECT t1.product_id AS product_id
SELECT CASE WHEN t1.NonebuyersNumber IS NULL
THEN 0
ELSE t1.NonebuyersNumber
END
AS NonebuyersNumber,
SELECT CASE WHEN t2.BuyersNumber IS NULL
THEN 0
ELSE t2.BuyersNumber
END
AS BuyersNumber
FROM
(SELECT
`table1`.`product_id` As product_id ,
COUNT(DISTINCT(table1.user_id)) AS NonebuyersNumber
FROM table1 WHERE status =1
AND `ispaid` != 2
GROUP BY `table1`.`product_id`)
AS t1
INNER JOIN
(SELECT
`table1`.`product_id` As product_id ,
COUNT(DISTINCT(table1.user_id)) AS BuyersNumber
FROM table1 WHERE `ispaid` = 1
GROUP BY `table1`.`product_id`)
AS t2
ON t1.product_id = t2.product_id
Basically, you need following
Join both the views on product_id
Use CASE statements in select in case one of the buyers numbers is NULL
I have this MySQL table named records. Below is its contents.
id record_id Data1 Data2 Time
1 1 null 1 1/1/16
2 1 1 null 1/3/16
3 1 2 null 1/4/16
4 1 null 3 1/5/16
5 2 1 null 2/1/16
6 2 1 null 2/3/16
7 2 7 null 2/4/16
8 2 null 5 2/5/16
I would like to have a MySQL query to retrieve the last non-null record of each column for each record_id. The result would look something like;
record_id Data1 Data2 Time
1 2 3 1/5/16
2 7 5 2/5/16
The tricky part to this problem is that multiple columns are involved.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT record_id, MAX(Time) AS Time
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Time = t2.Time
If you simply want the greatest value for the data and time columns, then see the answer given by #Matt. But your language makes it unclear what you really want.
Update:
Something like this might give the results you want:
SELECT a.record_id,
a.Data1,
b.Data2,
c.Time
FROM
(
SELECT t1.record_id,
t1.Data1
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data1 IS NULL THEN 0 ELSE id END) AS Data1Id
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.Data1Id
) a
INNER JOIN
(
SELECT t1.record_id,
t1.Data2
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data2 IS NULL THEN 0 ELSE id END) AS Data2Id
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.Data2Id
) b
ON a.record_id = b.record_id
INNER JOIN
(
SELECT t1.record_id,
t1.Time
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data2 IS NULL THEN 0 ELSE id END) AS TimeId
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.TimeId
) c
ON a.record_id = c.record_id
Demo Here:
SQLFiddle
This one may solve your problem:
select
record_id,
substring_index(group_concat(Data1 order by Time desc), ',', 1) Data1,
substring_index(group_concat(Data2 order by Time desc), ',', 1) Data2,
substring_index(group_concat(Time order by Time desc), ',', 1) Time
from records
group by record_id
;
It may not be as fast as other answers, but is another version... give it a try. If you have a Data3 column in your table, you can copy/paste the Data1 column and just change all references of this column to the new one.
Just to explain how this works: the group_concat function concatenates all non-null values of a column with a separator (, by default). You can order the column before the concatenation. It works a bit like a window function in Oracle, Postgre, and others... The substring_index is just getting the first concatenated value, as the list is in a descending order of time.
it looks like you are just wanting the maximum data1, max data2, and max time which would be simple aggregation:
SELECT
record_id
,MAX(Data1) as Data1
,MAX(Data2) as Data2
,MAX(Time) as Time
FROM
yourTable
GROUP BY
record_id
SQL fiddle for it http://www.sqlfiddle.com/#!9/d95bc1/2
If latest non-null value per column is desired you can use:
SELECT t.record_id, MAX(t.Data1) as Data1, MAX(t.Data2) as Data2, MAX(t.Time) as Time
FROM
yourTable t
LEFT JOIN
(
SELECT
record_id, MAX(Time) as MaxTime
FROM
yourTable t
WHERE
Data1 IS NOT NULL
GROUP BY
record_id
) d1
ON t.record_id = d1.record_id
AND t.Time = d1.MaxTime
LEFT JOIN
(
SELECT
record_id, MAX(Time) as MaxTime
FROM
yourTable t
WHERE
Data2 IS NOT NULL
GROUP BY
record_id
) d2
ON t.record_id = d2.record_id
AND t.Time = d2.MaxTime
WHERE
d1.record_id IS NOT NULL
OR d2.record_id IS NOT NULL
GROUP BY
t.record_id
Using Tim's method you can actually still get to your results looking at the Latest Data1 record and then the latest Data2 record and then aggregating so they are not purely the MAX of everything but rather representative of the latest 2 records 1 for Data1 and 1 for Data2.
SQL fiddle for this part: http://www.sqlfiddle.com/#!9/d95bc1/10
I would like to have a MySQL query to retrieve the last non-null record of each column for each record_id.
Of course, what is still somewhat unclear is how you determine that a row is the last row, since rows in a database are by definition unordered.
So, my interpretation is that you want the last non-null Data1, Data2 and Time column values for each distinct record_id value. And a row value is considered last if it has a higher id value than another row value.
Assuming my understanding is correct, the following query would work:
select t.record_id,
(select t2.Data1
from tbl t2
where t2.record_id = t.record_id
and t2.Data1 is not null
order by t2.id desc
limit 1) as Data1,
(select t2.Data2
from tbl t2
where t2.record_id = t.record_id
and t2.Data2 is not null
order by t2.id desc
limit 1) as Data2,
(select t2.Time
from tbl t2
where t2.record_id = t.record_id
and t2.Time is not null
order by t2.id desc
limit 1) as Time
from tbl t
group by t.record_id
order by t.record_id
SQLFiddle Demo
This problem is not present in MariaDB 10.6 anymore.
We have a problem with outer join and a function call inside a where condition. It seems that the function is not called if the outer join don't find records in the outer table.
In real the function are more complex this is simplify by eample to demonstrate the problem.
Simple Test Returns nothing, but 1 record would be correct:
SELECT *
FROM (SELECT 1 ID) t1
LEFT JOIN (SELECT 1 ID, null as date FROM (SELECT 1) x WHERE 1=2) t2 ON t2.ID=t1.ID
WHERE
IS_LOWHIGH_DATE(t2.date)
Complex Test also returns nothing, but one record would be correct:
SELECT *
FROM (SELECT 1 ID1, cast(null as date) as date1 ) t1
LEFT JOIN (SELECT 2 ID2, cast(null as date) as date2) t2 ON t2.ID2=t1.ID1
WHERE
IS_LOWHIGH_DATE(t2.date2)
Return one record with correct results:
SELECT *, IS_LOWHIGH_DATE(NULL), IS_LOWHIGH_DATE(t1.date1), IS_LOWHIGH_DATE(current_date)
FROM (SELECT CAST(NULL as date) as date1) t1
Should by return two records but don't do that, only one line returns:
SELECT t1.*, IS_LOWHIGH_DATE(t1.date1), NULL, NULL
FROM (SELECT 1 ID1, cast(null as date) as date1 ) t1
UNION ALL
SELECT t1.*, IS_LOWHIGH_DATE(t1.date1), t2.*
FROM (SELECT 1 ID1, cast(null as date) as date1 ) t1
LEFT JOIN (SELECT 2 ID2, cast(null as date) as date2) t2 ON t2.ID2=t1.ID1
WHERE
IS_LOWHIGH_DATE(t2.date2)
This works with coalesce() arround the attribute:
SELECT *
FROM (SELECT 1 ID) t1
LEFT JOIN (SELECT 1 ID, null as date FROM (SELECT 1) x WHERE 1=2) t2 ON t2.ID=t1.ID
WHERE
IS_LOWHIGH_DATE(COALESCE(t2.date))
Simple test with dummy return value (doesn't matter)
CREATE FUNCTION is_lowhigh_date
(p_date date) RETURNS tinyint(1)
NO SQL DETERMINISTIC
BEGIN
RETURN true;
END
The function at is
CREATE FUNCTION is_lowhigh_date
(p_date date) RETURNS tinyint(1)
NO SQL DETERMINISTIC
BEGIN
RETURN CASE
WHEN p_date is NULL OR p_date <= '0001-01-01' or p_date >= '9999-12-31' THEN
true
ELSE
false
END;
END
You can try sqlfiddle: http://sqlfiddle.com/#!2/513671/6
This will give you your expected result (avoiding the empty set caused by the last where)
SELECT * FROM (SELECT 1 ID) t1
LEFT JOIN (SELECT 1 ID, null as date FROM (SELECT 1) x WHERE 1=2) t2
ON (t2.ID=t1.ID AND IS_LOWHIGH_DATE(t2.date))
Run these commands:
explain extended
SELECT *
FROM (SELECT 1 ID) t1
LEFT JOIN (SELECT 1 ID, null as date FROM (SELECT 1) x WHERE 1=2) t2 ON t2.ID=t1.ID
WHERE
IS_LOWHIGH_DATE(t2.date)
;
show warnings;
this will give you your query after optimalizaltion
/* select#1 */ select '1' AS `ID`,NULL AS `ID`,NULL AS `date`
from (/* select#3 */ select 1 AS `ID`,NULL AS `date`
from (/* select#4 */ select 1 AS `1`) `x` where 0) `t2` where 0
The last WHERE 0 means WHERE false => MySql doesn't execute this query at all, so the subquery nor function in the subquery are not executed too.