Conditionally select from one table or another in MySQL - mysql

In MySQL, how do I select from another table if a foreign key is set?
What I'm trying to do is select Fields.value if Fields.value_id isn't set, otherwise select Values.value from Values where Value.id is equal to Fields.value_id.
My tables:
Fields:
id | value | value_id
Values:
id | value
What's wrong with my code here?
Code:
SELECT CASE
WHEN Field.value_id = NULL OR Field.value_id = ""
THEN Field.value
ELSE
Value.value
FROM values as Value
WHERE (Field.value_id = Value.id)

One syntax error is that you are missing the end in the case. I also think you want a left join between the tables. My best guess given the available information is this:
SELECT (CASE WHEN f.value_id = NULL OR f.value_id = ''
THEN f.value
ELSE v.value
END)
FROM fields f left join
values v
on f.value_id = v.id;

Related

mySQL Inclusive search on a flat table

I have the below SQL table, and i'm wondering if it's possible to have an inclusive search with this table format? Meaning if someone were to search for SSNU and ACCOUNT_NO it would only return the data if the CASE ID has an SSNU and ACCOUNT_NO that match the inputs.
I've tried a where clause like the below:
AND (a.KEY = "ACCOUNT_NO" AND a.VALUE = "XXXXXX")
AND (a.KEY = "GPID" AND a.VALUE = "XXXXX")
but have not had any luck.
Do you want to prioritize the row whose key is 'ACCOUNT_NO', and fallback on 'GPID'? If you want just one row, then you can use order by and limit:
select *
from mytable a
where a.key in ('ACCOUNT_NO', 'GPID') and a.value = 'XXXXXX'
order by a.key = 'ACCOUNT_NO' desc
limit 1
The order by clause puts first the row whose key is 'ACCOUNT_NO', if any. Note that I factorized the condition on VALUE, since it is common to both conditions.
If by "person" you mean "case id", then I think you want aggregation:
select case_id
from t
where (a.KEY = 'ACCOUNT_NO' AND a.VALUE = 'XXXXXX') or
(a.KEY = 'GPID' AND a.VALUE = 'XXXXX')
group by case_id
having count(distinct a.key) = 2;
This returns cases that have both the key/value pairs.

Select column from another table if it matches a content

I've a SELECT which checks a status of active alarms (icinga).
This select joins different tables and until here all ok.
On the result I've as value/column an object_id as well. I would like to add a column to that select that could be empty or not, because, searching that 'object_id' on a different table, I could get a value or not. This accessory table is structured having: object_id, varname, varvalue.
So, i.e., my SELECT returns those values:
`name`, `object_id`, `status`
`Hello`, `123456`, `OK`
I would add the column City that should compared to a table having:
`object_id`, `varname`, `varvalue`
`123456`, `city`, `Rome`
`123456`, `lake`, `Garda`
`789789`, `city`, `Milano`
So that if the second table has object_id = 123456 AND city = Rome the result should be:
`name`, `object_id`, `status`, `city`
`Hello`, `123456`, `OK`, `Rome`
Otherwise the result should be:
`Hello`, `123456`, `OK`, `UNKNOWN`
How to do that?
Hope I've explained it well :-)
Thanks!
* EDIT *
It's better I explain with real example. My query actually is the following:
select icinga_objects.object_id, icinga_objects.name1 as host_name, icinga_objects.name2 as ServiceName, "service" as Type, icinga_servicestatus.last_check as LastCheckTime, icinga_servicestatus.last_hard_state_change as LastStateChange, TIMEDIFF(now(), icinga_servicestatus.last_hard_state_change) AS SinceTime,
CASE
WHEN icinga_servicestatus.current_state = 0 THEN '0'
WHEN icinga_servicestatus.current_state = 1 THEN '2'
WHEN icinga_servicestatus.current_state = 2 THEN '3'
ELSE '3'
END AS state
FROM icinga_objects, icinga_servicestatus, icinga_services WHERE icinga_servicestatus.service_object_id IN
(SELECT service_object_id FROM icinga_services WHERE icinga_services.host_object_id IN
(SELECT host_object_id FROM icinga_hostgroup_members WHERE hostgroup_id IN
(SELECT hostgroup_id FROM icinga_hostgroups WHERE alias = 'MY-HOSTGROUP-TO-FILTER')
)
)
AND icinga_servicestatus.service_object_id NOT IN
(SELECT service_object_id FROM icinga_services WHERE icinga_services.service_object_id IN (
SELECT object_id FROM icinga_objects WHERE icinga_objects.is_active = 1 AND icinga_objects.object_id IN
(SELECT object_id FROM icinga_customvariables WHERE varvalue = '8x5')
)
)
AND icinga_servicestatus.last_check > NOW() - INTERVAL 3 HOUR
AND icinga_servicestatus.state_type = 1
AND icinga_servicestatus.scheduled_downtime_depth = 0
AND icinga_objects.object_id = icinga_services.service_object_id
AND icinga_servicestatus.service_object_id = icinga_services.service_object_id
AND icinga_servicestatus.current_state = 2
AND icinga_servicestatus.problem_has_been_acknowledged = 0
This gives me as result, in example:
`object_id`, `host_name`, `ServiceName`, `Type`, `LastCheckTime`, `LastStateChange`, `SinceTime`, `State`
`123456`, `myHostName`, `myServiceName`, `service`, `2020-04-29 17:19:21`, `2020-04-28 14:50:27`, `26:32:51`, `3`
Here I would like to add the column.
So, now if I search object_id into icinga_customvariables I could find entries, or not. In Example, searching object_id = 123456 I have 4 records, but ONLY one having varname = NAME_IM_SEARCHING and so I need to add to the above result the corresponding of varvalue searching icinga_customvariables.object_id = '123456' AND varname = NAME_IM_SEARCHING. IF there are NO results, then the added cloumn should be UNKNOWN, otherwise the added column should be = icinga_customvariables.varvalue.
How to add it? :-)
You can place your query into a "table expression" so it becomes simpler to join it to the other_table. For example:
select
q.*,
coalesce(o.varvalue, 'UNKNOWN') as city
from (
-- your existing query here
) q
left join other_table o on o.object_id = q.object_id and o.varname = 'city'
EDIT: Joining multiple times
As requested if you need to extract more city names using another column, or if you want to extract against another table altogether, you can add an extra LEFT JOIN. For example:
select
q.*,
coalesce(o.varvalue, 'UNKNOWN') as city,
coalesce(o2.varvalue, 'UNKNOWN') as lake
from (
-- your existing query here
) q
left join other_table o on o.object_id = q.object_id and o.varname = 'city'
left join other_table o2 on o.object_id = q.object_id and o2.varname = 'lake'

AND clause on multiple values for same column

How can I fix this query?
SELECT COUNT(*)
FROM user_notifications
WHERE notification_status = '0'
AND notification_sent_to = ?
AND notification_category = 'ticket reply'
AND notification_category = 'groupdoc'
I need to count result filtering only notification category for ticket reply and groupdoc.
Many thanks for your help.
A particular column can have only one value in a given row. So the following condition can never be True
notification_category = 'ticket reply'AND notification_category= 'groupdoc'
When people think I need rows with value of 'a' and rows with value of 'b', the actual logical operator to use is OR
SELECT COUNT(*) FROM user_notifications WHERE notification_status = '0' AND notification_sent_to=? AND
(notification_category = 'ticket reply' OR notification_category= 'groupdoc')
A column can not have 2 values at same time. I think what you want is an OR
SELECT COUNT(*)
FROM user_notifications
WHERE notification_status = 0 AND notification_sent_to=?
AND (notification_category = 'ticket reply' OR notification_category= 'groupdoc')
Use in:
SELECT COUNT(*)
FROM user_notifications
WHERE notification_status = 0 AND
notification_sent_to = ? AND
notification_category IN ('ticket reply', 'groupdoc');

MYSQL SELECT is slow when crossing multiple tables

I have a mysql query which is to return the only 1 record that need to cross multiple table. However, the mysql query is slow when executing.
Query:
SELECT *,
(SELECT TreeName FROM sys_tree WHERE TreeId = Mktg_Unit_Booking.ProjectLevelId) AS PhaseName,
(CASE WHEN ProductType = 'U' THEN (SELECT UnitNo FROM prop_unit pu WHERE pu.UnitId = mktg_unit_booking.UnitId)
ELSE (SELECT BayNo FROM prop_car_park pcp WHERE pcp.CarParkId = UnitId) END) AS UnitNo,
(SELECT CustomerName FROM mktg_customer mc WHERE mc.CustomerId = mktg_unit_booking.CustomerId) AS CustomerName
FROM Mktg_Unit_Booking
WHERE IsDeleted <> '1' AND IsApproved = '1'
AND UnitId = 1110 AND ProductType = 'U'
ORDER BY UnitNo
I have run EXPLAIN in the query and I got this:
Any other suggestion on how to improve the speed of the query?
Thank you!
you are doing the cross product, instead of that you should use join.
Don't use sub-queries in select statement instead use proper join on Mktg_Unit_Booking in after from statement.
you query should something look like :
select
sys_tree.TreeName AS PhaseName,
case
WHEN Mktg_Unit_Booking.ProductType = 'U' then prop_unit.UnitNo
else prop_car_park.BayNo
end as UnitNo,
mktg_customer.CustomerName AS CustomerName
FROM Mktg_Unit_Booking
left join sys_tree on sys_tree.TreeId = Mktg_Unit_Booking.ProjectLevelId
left join prop_unit on prop_unit.UnitId = Mktg_Unit_Booking.UnitId
left join prop_car_park on prop_car_park.CarParkId = Mktg_Unit_Booking.UnitId
left join mktg_customer on mktg_customer.CustomerId = Mktg_Unit_Booking.CustomerId
WHERE IsDeleted <> '1' AND IsApproved = '1'
AND UnitId = 1110 AND ProductType = 'U'
ORDER BY UnitNo;
I have assumed that each table consists of only 1 matching tuple. If there are more then your logic needs to be modified.

SQL , get difference b/w to id's

I want to get result from two tables:
Table A
create table A
( propertyId int not null
,PRIMARY KEY (propertyId))
Table B
create table B
( Id int not null
, propertyId int
, FOREIGN KEY (propertyId ) REFERENCES A(propertyId))
Now I want to get the result total count id of Table A exist in Table B AND total count id that does not exist in Table B
SELECT COUNT(property.propertyId) AS 'Occupied'
,(
SELECT COUNT(property.propertyId)
FROM property
INNER JOIN agreement ON property.`propertyId` <> agreement.`propertyId`
) AS 'Vacant'
FROM property
INNER JOIN agreement ON property.`propertyId` = agreement.`propertyId`
WHERE agreement.`isActive` = '1'
You can do a Left join to get the proper result.
select
sum (
case when a.propertyId is not null then 1
else 0
end
) as present_cnt,
sum (
case when a.propertyId is null then 1
else 0
end
) as not_present_cnt
from property p
left join agreement a on p.propertyId = a.propertyId
where a.isActive = '1';
left join will fetch data for a.propertyId from agreement corresponding to p.propertyId, if no match found then null
Your solution should be based on this principle, also exemplified in this SQLFiddle:
SELECT count(CASE
WHEN propertyID IS NOT NULL
AND fk_propertyID IS NOT NULL
THEN 1
ELSE NULL
END) 'exist_in_both_tables'
,count(CASE
WHEN propertyID IS NOT NULL
AND fk_propertyID IS NULL
THEN 1
ELSE NULL
END) 'does_not_exist_in_b'
FROM (
SELECT *
FROM tablea a
LEFT JOIN tableb b ON a.propertyID = b.fk_propertyID
UNION
SELECT *
FROM tablea a
RIGHT JOIN tableb b ON a.propertyID = b.fk_propertyID
) result
You need to duplicate your query and use both LEFT and RIGHT joins and UNION the results of those two separate queries to simulate the result of a FULL JOIN.
After simulating this, you need to go over this result set again, by making this a sub-query and then using aggregate functions, COUNT(), along with CASE statements to count how many matches you have in both tables.
Hence, your final queries should look something like this:
SELECT count(CASE
WHEN Property_PropertyID IS NOT NULL
AND Agreement_PropertyID IS NOT NULL
THEN 1
ELSE NULL
END) 'Occupied'
,count(CASE
WHEN Property_PropertyID IS NOT NULL
AND Agreement_PropertyID IS NULL
THEN 1
ELSE NULL
END) 'Vacant'
FROM (
SELECT property.propertyID 'Property_PropertyID'
FROM property
LEFT JOIN agreement ON property.`propertyId` = agreement.`propertyId`
WHERE agreement.`isActive` = '1'
UNION
SELECT agreement.propertyID 'Agreement_PropertyID'
FROM property
RIGHT JOIN agreement ON property.`propertyId` = agreement.`propertyId`
WHERE agreement.`isActive` = '1'
) ResultSet