Selecting a value from a table with a dynamically where statement - mysql

I have the following 4 tables
table a:
id,
name,
b_id,
c_id
table b:
id,
name,
d_id
table c:
id,
name,
d_id
table d:
id,
name
Then I have my query, as far as I can write it:
SELECT a.name as a_name, b.name as b_name, c.name as c_name, d.name as d_name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON a.c_id = c.id
I need to add a LEFT JOIN addressing table ā€œdā€ with an ON that is dynamic.
It shall either be
LEFT JOIN d ON b.d_id = d.id (if a.b_id != 0)
or
LEFT JOIN d ON c.d_id = d.id (if a.b_id == 0)
I tried to write
SELECT a.name as a_name, b.name as b_name, c.name as c_name, d.name as d_name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON a.c_id = c.id
if((if a.b_id != 0,LEFT JOIN d ON b.d_id = d.id, LEFT JOIN d ON c.d_id = d.id )
but that throws the error
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'IF(a.b_id != 0, LEFT JOIN d ON b.d_id = d.id, LEFT JOIN d ON c.d_id = d.id)' at line 6"

You may try:
SELECT a.name as a_name, b.name as b_name, c.name as c_name, d.name as d_name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON a.c_id = c.id
LEFT JOIN
ON (a.b_id <> 0 AND b.d_id = d.id) OR (a.b_id = 0 AND c.d_id = d.id);

You can't dynamically add join clauses like that, but you can emulate the functionality inside the join condition with some logical operators:
SELECT a.name as a_name, b.name as b_name, c.name as c_name, d.name as d_name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON a.c_id = c.id
LEFT JOIN d ON (a.b_id != 0 AND b.d_id = d.id) OR (a.b_id = 0 AND c.d_id = d.id)

Well, you can use:
FROM a LEFT JOIN
b
ON a.b_id = b.id LEFT JOIN
c
ON a.c_id = c.id LEFT JOIN
d
ON (a.b_id <> 0 AND b.d_id = d.id) OR
(a.b_id = 0 AND c.d_id = d.id)
This doesn't take NULL values into account. If that is needed:
ON (a.b_id <> 0 AND b.d_id = d.id) OR
(COALESCE(a.b_id, 0) = 0 AND c.d_id = d.id)
In SQL, CASE is an expression that returns a value. It is not a macro where code is replaced.
Also, this answers the question that you asked here. However, this might not be the most performant way to accomplish what you want. If you also need help with performance, ask a new question. Provide sample data, desired results, and an explanation of the logic you want to implement. A DB/SQL Fiddle also helps.

Related

SQL query taking too long - multiple ANDs with the same ID

I am not sure why but this query (even when there are two results) is taking too long to execute.
What I am trying to do is that users c table's UID (primary key) matches with entity_id of all the other tables (foreign key). That's all I have to do.
I guess the problem is that there are too many foreign keys I have to match my primary key.
SELECT
f.field_fmno_value as fo,
fn.field_full_name_value as fullname,
c.mail as email,
t.field_type_value as persontype,
ti.field_title_value as persontitle,
sc.field_skill_code_value as skillcode,
sg.field_skill_group_value as skillgroup,
dn.field_department_value as deptt,
r.field_region_for_real_value as region,
cl.field_region_value as citylocation,
goc.field_goc_office_value as goc,
hu.field_hub_value as hub,
ge.field_gender_value as gender,
ft.field_full_tenure_value as tenure,
tsa.field_tenure_since_associate_value as tass,
b.category as category,
b.subcategory as subcategory,
b.section as industry,
b.title as title,
a.browser_platform, b.path, b.title, a.referer, a.time, a.id
FROM
track_da_files a, track_da_files_paths b, users c, field_data_field_region_for_real r,
field_data_field_fo f,
field_data_field_full_name fn,
field_data_field_type t,
field_data_field_title ti,
field_data_field_s_code sc,
field_data_field_s_group sg,
field_data_field_department dn,
field_data_field_region cl,
field_data_field_g_office goc,
field_data_field_hub hu,
field_data_field_gender ge,
field_data_field_full_ten ft,
field_data_field_ten_since_associate tsa
where a.pid = b.pid AND a.uid = c.uid AND a.uid <> 0
AND c.uid = r.entity_id
AND c.uid = fn.entity_id
AND c.uid = f.entity_id
AND c.uid = t.entity_id
AND c.uid = ti.entity_id
AND c.uid = sc.entity_id
AND c.uid = sg.entity_id
AND c.uid = dn.entity_id
AND c.uid = cl.entity_id
AND c.uid = goc.entity_id
AND c.uid = hu.entity_id
AND c.uid = ge.entity_id
AND c.uid = ft.entity_id
AND c.uid = tsa.entity_id
GROUP a.pid
ORDER BY 1 DESC
Revised query to JOIN format... Also, GROUP BY appears irrelevant as you are not doing any aggregations. If you have multiple records possible at any of the child-level tables, that could force multiple records, but only for those instances. If everything is a 1:1 ratio of records, you should be fine. If 1:n ration in more than 1 child table you will get a Cartesian result and could bloat (such as multiple skill groups).
Also, since a.uid = c.uid, you could also use the a.uid to all the underlying tables too so they all appear as child-tables. I would assume all these child tables are lookup tables and ALL of them have an index on the respective field representing the "uid" it is joined based on.
SELECT
f.field_fmno_value as fo,
fn.field_full_name_value as fullname,
c.mail as email,
t.field_type_value as persontype,
ti.field_title_value as persontitle,
sc.field_skill_code_value as skillcode,
sg.field_skill_group_value as skillgroup,
dn.field_department_value as deptt,
r.field_region_for_real_value as region,
cl.field_region_value as citylocation,
goc.field_goc_office_value as goc,
hu.field_hub_value as hub,
ge.field_gender_value as gender,
ft.field_full_tenure_value as tenure,
tsa.field_tenure_since_associate_value as tass,
b.category as category,
b.subcategory as subcategory,
b.section as industry,
b.title as title,
a.browser_platform,
b.path,
b.title,
a.referer,
a.time,
a.id
FROM
track_da_files a
JOIN track_da_files_paths b
ON a.pid = b.pid
JOIN users c
ON a.uid = c.uid
JOIN field_data_field_region_for_real r
ON a.uid = r.entity_id
JOIN field_data_field_fo f
ON a.uid = f.entity_id
JOIN field_data_field_full_name fn
ON a.uid = fn.entity_id
JOIN field_data_field_type t,
ON a.uid = t.entity_id
JOIN field_data_field_title ti,
ON a.uid = ti.entity_id
JOIN field_data_field_s_code sc,
ON a.uid = sc.entity_id
JOIN field_data_field_s_group sg,
ON a.uid = sg.entity_id
JOIN field_data_field_department dn,
ON a.uid = dn.entity_id
JOIN field_data_field_region cl,
ON a.uid = cl.entity_id
JOIN field_data_field_g_office goc,
ON a.uid = goc.entity_id
JOIN field_data_field_hub hu,
ON a.uid = hu.entity_id
JOIN field_data_field_gender ge,
ON a.uid = ge.entity_id
JOIN field_data_field_full_ten ft,
ON a.uid = ft.entity_id
JOIN field_data_field_ten_since_associate tsa
ON a.uid = tsa.entity_id
where
a.uid <> 0
GROUP BY
a.pid
ORDER BY
1 DESC
One final possibility on this query under MySQL is
SELECT STRAIGHT_JOIN
f.field_fmno_value as fo ...(rest of query).
STRAIGHT_JOIN tells MySQL to do the query in the table order I provided since everything looks like a child lookup, you want the driving table to be the primary "a" table reference.

Why am I getting duplicates in a join between four tables?

tables and query are here
http://sqlfiddle.com/#!9/2b6f35a/1/0
In it for each name i get its tags title, So it would be like this
name1: title1;
name2: title1;
name3: title1;
My problem is that i get double the tags
name1: title1, title1;
name2: title1, title1;
name3: title1;
What is the mistake i've made?
The entire problem comes for tablx
Sorry about the mess before, didn't know about sqlfiddle
The join with X table is returning the 4th row. http://sqlfiddle.com/#!9/2b6f35a/6, which draws the same title1 value in from the tags table, which is then aggregated into the final response with GROUP_CONCAT()
A solution if you need to join both these tables in one query but only want the title1 one time is to remove the GROUP_CONCAT() aggregator: http://sqlfiddle.com/#!9/2b6f35a/7
SELECT a.name, x.rate, c.title
FROM tabl1 a
LEFT JOIN tablx x ON x.pid = a.id
INNER JOIN tabl2 b ON a.id = b.pid
INNER JOIN tabl3 c ON c.id = b.bid
WHERE c.title IN ('title1')
GROUP BY a.id
In your case, it might be more useful to have an aggregator on the rate column for x table, like so: http://sqlfiddle.com/#!9/2b6f35a/9
SELECT a.name, x.rate, c.title, SUM(x.rate) AS rate_sum
FROM tabl1 a
LEFT JOIN tablx x ON x.pid = a.id
INNER JOIN tabl2 b ON a.id = b.pid
INNER JOIN tabl3 c ON c.id = b.bid
WHERE c.title IN ('title1')
GROUP BY a.id
If you just want to count the number of distinct tags in this situation, you can use COUNT(DISTINCT...). http://sqlfiddle.com/#!9/2b6f35a/15:
SELECT a.name, b.id as bid, c.title, x.id as xid, x.rate, c.title, SUM(x.rate) AS rate_sum, COUNT(DISTINCT c.title) as title_count
FROM tabl1 a
LEFT JOIN tablx x ON x.pid = a.id
INNER JOIN tabl2 b ON a.id = b.pid
INNER JOIN tabl3 c ON c.id = b.bid
WHERE c.title IN ('title1')
GROUP BY a.id
If everything in the posted question is correct, you aren't doing anything wrong.
See this sqlfiddle:
http://sqlfiddle.com/#!9/03205d/1

SQL: join select multiple tables with missing row

I have a big multiple table query join select where some values are optional.
This is the query:
SELECT a.date_in, a.date_out, b.name, b.phone, b.birthdate,
b.country, b.hotel, b.room_nr, b.passport_nr, c.email,
d.size, e.name, GROUP_CONCAT(DISTINCT g.service), GROUP_CONCAT(DISTINCT h.service)
, GROUP_CONCAT(DISTINCT i.time), GROUP_CONCAT(DISTINCT j.location)
FROM reservation a, rider b, user c,
bike_size d, bike e, services_reservation f, services g,
bike_shipping h, bike_shipping_reservation i
, bike_shipping_location j WHERE a.rider_id = b.id
AND b.user_id = c.id AND a.bike_size_id = d.id AND
d.bike_id = e.id AND a.id = f.reservation_id AND
f.services_id = g.id
AND h.id = i.bike_shipping_id AND a.id = i.reservation_id
AND i.bike_shipping_location_id = j.id
AND a.id = 80
In the tables from the query above, the table named services_reservation with the following columns (id, services_id, reservation_id) is completely empty in this case, which makes the values that I select from the table bike_shipping_reservation NULL.
How can I make some tables that I select from optional in case they are empty?
Here is the SQL Fiddle with 1 empty table, you can see the NULL results at the end (only GROUP_CONCAT(DISTINCT g.service) should be NULL).
http://sqlfiddle.com/#!9/ee31b/6
Here is the SQL Fiddle with all tables having values in there columns, you can see that all values are returned not NULL.
http://sqlfiddle.com/#!9/8bc033/34
Any thoughts?
Use left join where the table (or the row) are empty on don't match
SELECT a.date_in, a.date_out, b.name, b.phone, b.birthdate,
b.country, b.hotel, b.room_nr, b.passport_nr, c.email,
d.size, e.name, GROUP_CONCAT(DISTINCT g.service), GROUP_CONCAT(DISTINCT h.service)
, GROUP_CONCAT(DISTINCT i.time), GROUP_CONCAT(DISTINCT j.location)
FROM reservation a
INNER JOIN rider b on a.rider_id = b.id
INNER JOIN user c on b.user_id = c.id
INNER JOIN bike_size d on a.bike_size_id = d.id
INNER JOIN bike e ON d.bike_id = e.id
LEFT JOIN services_reservation f on a.id = f.reservation_id
INNER JOIN services g on f.services_id = g.id
INNER JOIN bike_shipping_reservation i on a.id = i.reservation_id
INNER JOIN bike_shipping h ON h.id = i.bike_shipping_id
INNER JOIN bike_shipping_location j on i.bike_shipping_location_id = j.id
where a.id = 80

Mysql inner join twice and use WHERE on both joins

In MySQL, I want to SELECT A.* FROM A where an inner join condition is satisfied whether directly (joining table B) or through another join table (C), WHERE B.field = myvalue. Can anyone point out the proper way to get results?
I have the following tables: A, B, C, which are associated as follows (A joins B, B joins C, A joins C):
B
/ \
A --- C
It looks pretty straightforward, but I get an empty set when I run the following code, even though I get results when I restrict the search to just joining B through C:
SELECT A.* FROM A
INNER JOIN C ON C.id = A.c_id
INNER JOIN B AS B_thru_C ON B_thru_C.id = C.b_id
INNER JOIN B AS B_from_A ON B_from_A.id = A.b_id
WHERE B_thru_C.field = 'myvalue' OR B_from_A.field = 'myvalue';
# yields an empty set
SELECT A.* FROM A
INNER JOIN C ON C.id = A.c_id
INNER JOIN B AS B_thru_C ON B_thru_C.id = C.b_id
WHERE B_thru_C.field = 'myvalue';
# yields results
How about this?
SELECT A.* FROM A
LEFT OUTER JOIN C ON C.id = A.c_id
INNER JOIN B ON B.id = A.b_id OR B.id = C.b_id
WHERE B.field = 'myvalue';

Optimizing this SQL Query

How would you optimize the following query?
'example_companies' contains companies data.
'example_roles_companies' contains companies roles (pivot)
'example_industries_companies' contains companies industries (pivot)
SELECT DISTINCT a.id,
a.mode,
a.name,
a.city,
b.name AS USER,
b.phone
FROM example_companies a
LEFT JOIN example_users b
ON a.contact_id = b.id
LEFT JOIN example_roles_companies c
ON a.id = c.company_id
WHERE "2" IN (SELECT industry_id
FROM example_industries_companies
WHERE company_id = a.id)
AND c.role_id = 2
AND a.account_mode != 2
ORDER BY a.id
Query:
SELECT DISTINCT a.id,
a.mode,
a.name,
a.city,
b.name AS USER,
b.phone
FROM example_companies a
LEFT JOIN example_users b ON a.contact_id = b.id
INNER JOIN example_roles_companies c ON a.id = c.company_id AND c.role_id = 2
INNER JOIN example_industries_companies i
ON i.company_id = a.id AND i.industry_id = "2"
WHERE
a.account_mode != 2
ORDER BY
a.id
Structure:
Index on a.id, not null
Index on b.id, not null [analyze the opportunity of adding another index (b.id, b.name, b.phone) to this table as well]
Index on (c.company_id, c.role_id) not null both
Index on (i.company_id, i.industry_id), not null both
Remarks:
Please note that your industry_id = "2" seems weird to me, ids are generally numbers and if they are not then it should be looked since integers are faster to process than strings. Additionally, this way of double-quoting is not usual in mysql. Are you sure of your syntax?