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

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.

Related

Selecting a value from a table with a dynamically where statement

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.

Error with ORDER BY clause using UNION in MySQL

I have the following query in MySQL:
(SELECT ue.id, ue.userid, ue.status, ue.timestart, ue.timeend, e.courseid,
e.id AS enrolid, ra.roleid
FROM user_enrolments ue
JOIN enrol e ON e.id = ue.enrolid
JOIN course c ON c.id = e.courseid
JOIN user u ON u.id = ue.userid
JOIN context ct ON ct.instanceid = c.id
LEFT JOIN role_assignments ra ON ra.userid = u.id AND
ra.contextid = ct.id AND
ra.itemid = e.id
WHERE e.customint1 = 1 AND u.deleted = 0 AND
ct.contextlevel = 50 AND (ue.status = 0 OR ue.status = 1))
UNION
(SELECT de.enrolid AS id, de.userid, de.status, de.date_ini, de.date_fin,
de.courseid, de.enrolid, de.roleid
FROM deleted_enrols de
JOIN user u ON u.id = de.userid
WHERE userid = ANY (SELECT userid FROM local_users WHERE clientid = 1))
ORDER BY u.firstname, u.lastname, c.fullname LIMIT 0, 100
If I delete ORBER BY and LIMIT, this query works fine... but the ORDER BY clause gives an error:
Table 'u' from one of the SELECTs cannot be used in global ORDER clause
If I delete the parentheses of both SELECT querys, the error is different:
Table 'u' from one of the SELECTs cannot be used in field list
I have also tried with UNION ALL, but it does not work either.
Any suggestion or clue? Thanks in advance for your time...
The results of your UNION do not include any fields from table 'u', so those results cannot be sorted by table 'u' fields.
You could perhaps perform the UNION and then re-join the results to table 'u', and then use that to sort the results by table 'u' fields. A similar issue exists for sorting on
course.fullname, so that would need to be joined back in, too.
SELECT x.id, x.userid, x.status, x.timestart, x.timeend, x.courseid, x.enrolid, x.roleid
FROM ((SELECT ue.id, ue.userid, ue.status, ue.timestart, ue.timeend, e.courseid,
e.id AS enrolid, ra.roleid
FROM user_enrolments ue
JOIN enrol e ON e.id = ue.enrolid
JOIN course c ON c.id = e.courseid
JOIN user u ON u.id = ue.userid
JOIN context ct ON ct.instanceid = c.id
LEFT JOIN role_assignments ra ON ra.userid = u.id
AND ra.contextid = ct.id
AND ra.itemid = e.id
WHERE e.customint1 = 1 AND u.deleted = 0
AND ct.contextlevel = 50 AND (ue.status = 0 OR ue.status = 1))
UNION
(SELECT de.enrolid AS id, de.userid, de.status, de.date_ini, de.date_fin,
de.courseid, de.enrolid, de.roleid
FROM deleted_enrols de
JOIN user u ON u.id = de.userid
WHERE userid = ANY (SELECT userid FROM local_users WHERE clientid = 1))
) x
JOIN user z ON z.id = x.userid
JOIN course d ON d.id = x.courseid
ORDER BY z.firstname, z.lastname, d.fullname LIMIT 0, 100
Assuming you want to sort the whole lot, try parentheses round the whole query with the ORDER BY done afterwards:
select id, userid, status, timestart, timeend, courseid, enrolid, roleid from
((SELECT ue.id, ue.userid, ue.status, ue.timestart, ue.timeend, e.courseid,
e.id AS enrolid, ra.roleid, u.firstname, u.lastname, c.fullname
FROM user_enrolments ue
JOIN enrol e ON e.id = ue.enrolid
JOIN course c ON c.id = e.courseid
JOIN user u ON u.id = ue.userid
JOIN context ct ON ct.instanceid = c.id
LEFT JOIN role_assignments ra ON ra.userid = u.id AND
ra.contextid = ct.id AND
ra.itemid = e.id
WHERE e.customint1 = 1 AND u.deleted = 0 AND
ct.contextlevel = 50 AND (ue.status = 0 OR ue.status = 1))
UNION
(SELECT de.enrolid AS id, de.userid, de.status, de.date_ini, de.date_fin,
de.courseid, de.enrolid, de.roleid, u.firstname, u.lastname, ' ' as fullname
FROM deleted_enrols de
JOIN user u ON u.id = de.userid
WHERE userid = ANY (SELECT userid FROM local_users WHERE clientid = 1))) s1
ORDER BY firstname, lastname, fullname LIMIT 0, 100
(obviously fullname in the second SELECT statement would be populated however seems sensible)
You need to include the data to be ordered by in the selects of the unioned queries; an ORDER BY following a UNION is handled as if it were SELECT * FROM (unions) ORDER BY ... so anything not coming out of the union cannot be used for ordering.
Ironically, a query similar to that is the key to getting what you want though, with something like
SELECT x, y, z
FROM (
SELECT x, y, z, somethingIdontactuallywant
FROM blah
UNION
SELECT a, b, c, somethingIdontactuallywant
FROM blah2
) AS u
ORDER BY u.somethingIdontactuallywant
As mysql documentation on union says:
This kind of ORDER BY cannot use column references that include a
table name (that is, names in tbl_name.col_name format). Instead,
provide a column alias in the first SELECT statement and refer to the
alias in the ORDER BY. (Alternatively, refer to the column in the
ORDER BY using its column position. However, use of column positions
is deprecated.)
Also, if a column to be sorted is aliased, the ORDER BY clause must
refer to the alias, not the column name.
So, do not refer to any table names and use columns that are actually in the resultset of the union.

mysqli inner join: data not exist in other table

I have 3 tables and I am using inner join to get the data from table.
select b.pic,b.fname,b.lname, a.job_id, a.uid, a.job_title, a.description, a.city, a.position,
a.salary, a.looking_for,a.website,a.date
from job_posting a
inner join users b
inner join job_applied c on a.uid = b.uid and b.uid = c.uid
where c.job_id IS NULL and c.uid IS NULL and a.looking_for = 'student'
Above query is not returning any data. It's empty.
Let me explain the table.
job_posting - Contain job information.
users - contain user information who posted the job.
job_applied - contain user has applied for the jobs.
So, I want to get the jobs those are not applied. In Job_paplied table I have job_id and uid.
Instead of inner join try LEFT JOIN like:
select b.pic,b.fname,b.lname, a.job_id, a.uid, a.job_title,a.description,a.city, a.position,
a.salary, a.looking_for,a.website,a.date from job_posting a
LEFT JOIN users b on a.uid = b.uid
LEFT JOIN job_applied c on b.uid = c.uid
where c.job_id IS NULL and a.looking_for = 'student'
and try again.

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

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?