I have a very old project which uses MySQL, which I am considering converting part of to MS Access. I'm running into problems with some of the more complex queries, and wondered if there is a reference which details the differences between Access's SQL and MySQL. For example, I have the following query:
select P.PersonID, P.FirstName, P.MiddleName, P.LastName,
PR.LastName as MarriedName, P.Born, LocID, PlaceName,
City, County, State, Country
from persons P
left join relatives R on (R.Person = P.PersonID and TookName)
left join persons PR on (PR.PersonID = R.Relative)
left join locations L on (L.Person = P.PersonID and L.FromDate = P.Born)
where not P.Deleted
and (P.FirstName in ('Alan','Albert','Alfred','Allan','Allen','Alvin','Al')
or P.MiddleName in ('Alan','Albert','Alfred','Allan','Allen','Alvin','Al')
or P.Nickname in ('Alan','Albert','Alfred','Allan','Allen','Alvin','Al'))
and (P.LastName = 'Little' or PR.LastName = 'Little')
group by P.PersonID
order by P.Born desc
In Access, I can get as far as the first join:
select P.PersonID, P.FirstName, P.MiddleName, P.LastName,
PR.LastName as MarriedName, P.Born
from persons P
left join relatives R on (R.Person = P.PersonID and TookName)
where not P.Deleted
and P.FirstName in ('Alan','Albert','Alfred','Allan','Allen','Alvin','Al')
if I add the second join it says, Syntax error (missing operator) in query expression '(R.Person = P.PersonID and TookName) left join persons PR on (PR.PersonID = R.Relative.'
Clicking the Help button very helpfully informs me, The expression you typed is not valid for the reason indicated in the message. Gee thanks!
But I have some other rather complex queries, so beyond solving the problem with this one, I'm looking for something that will explain the differences in general.
EDIT:
So, I changed the query according to the answer linked to:
select P.PersonID, P.FirstName, P.MiddleName, P.LastName,
PR.LastName as MarriedName, P.Born
from (persons P
left join relatives R on R.Person = P.PersonID and TookName=true)
left join persons PR on PR.PersonID = R.Relative
where not P.Deleted
and P.FirstName in ('Alan','Albert','Alfred','Allan','Allen','Alvin','Al')
It tells me JOIN expression not supported, and highlights TookName=true. I also tried it as TookName=1 and just TookName. I tried removing the second JOIN, with the first in parentheses, and it still just tells me JOIN expression not supported.
The Access SQL parser is a huge fan of parentheses.
They are needed in all JOINs with more than 2 tables
FROM (a JOIN b ON a.id = b.id) JOIN c on b.id = c.id
so that only two tables / subqueries are joined in one set of parentheses.
And (as I learned today) they are needed around the ON clause if you want to use literal values in it.
FROM (a JOIN b ON (a.id = b.id AND b.foo = True)) JOIN c on b.id = c.id
An extended description is here. Link was found here: https://stackoverflow.com/a/23632282/3820271
the part left join relatives R on (R.Person = P.PersonID and TookName)
seems not complete (or not a valid sql expression)
tookName is not compared with nothings
could be you need somthings like :
left join relatives R on R.Person = P.PersonID and R.TookName = P.TookName
or
left join relatives R on R.Person = P.PersonID and R.TookName = 'FIXED VALUE'
or
left join relatives R on R.Person = P.PersonID and R.TookName is not null
for cross platform where on boolean you should use
left join relatives R on R.Person = P.PersonID and R.TookName =1
or better WHERE your_column <> 0
Related
I have some difficuties to get orders count with the following SQL query:
select
d.id,
d.title,
count(distinct o.id)
from store s
left join `order` o on o.store_id = s.id
left join order_product op on op.order_id=o.id
left join store_product sp on sp.id = op.product_id
left join product p on p.id = sp.product_id
left join department_category_to_entity dce1 on dce1.entity_type IN ('Product') and dce1.entity_id = p.id
left join department_category_to_entity dce2 on op.status != 'replaced' and
op.replacement_id is null and
dce2.entity_type IN ('StoreProduct') and
dce2.entity_id = sp.id
left join department_category_to_entity dce3 on op.status = 'replaced' and
op.replacement_id is not null and
dce3.entity_type IN ('StoreProduct') and
dce3.entity_id = op.replacement_id
left join department_category dc on dc.id = p.department_category_id or
dc.id = dce1.category_id or
dc.id = dce2.category_id or
dc.id = dce3.category_id
left join department d on d.id = dc.department_id
where d.id is not null
group by d.id;
Is it possible to get orders count without sub-queries or to get correct count of orders? Please, help... Thank you!
You have LEFT JOIN, which says to keep looking even if there is no row in the 'right' table. But, on the other hand, you are GROUPing BY a column in the last of a chain of LEFT JOINs! Perhaps you meant JOIN instead of LEFT JOIN??
Saying where d.id is not null is roughly equivalent to saying "Oops, all those LEFT JOINs could have been JOINs.
With GROUP BY and JOINs (LEFT or otherwise), you are doing an "inflate-deflate". What logically happens is all the JOINing is done to build a huge intermediate table with all the valid combinations. Then the COUNT(*) and GROUP BY are done. This tends to make the COUNTs (and SUMs, etc) have bigger values than expected.
What's the most direct route to get from department to order? It does not seem to involve store, so get rid of that table.
Are other tables irrelevant?
Even after addressing those issue, you still may be getting the wrong value. Please provide, for starters, `SHOW CREATE TABLE for each table.
I'm currently reading a query that I don't usually see in some other queries when joining tables.
SELECT
*
FROM table_1
RIGHT OUTER JOIN table_2
INNER JOIN table_3
ON table_2.some_id = table_3.some_id
RIGHT OUTER JOIN table_4
ON table_3.some_id = table_4.some_id
LEFT OUTER JOIN table_5
ON table_4.some_id = table_5.some_id
ON table_1.some_id = table_4.some_id
Please don't mind how those tables being joined. I just want to know how did that query works? Thank you in advance.
I think the SQL Server documentation captures what is happening better than MySQL. (Here is the documentation.)
This is a parsing issue, as Laurence has observed. You can understand what is by looking at the syntax diagram for the from statement. There is a recursive reference that most people never think about (including me). A joined table has the following syntax:
<joined_table> ::=
{
<table_source> <join_type> <table_source> ON <search_condition>
| <table_source> CROSS JOIN <table_source>
| left_table_source { CROSS | OUTER } APPLY right_table_source
| [ ( ] <joined_table> [ ) ]
}
The key here is the first piece, <table_source> <join_type> <table_source>. Well, guess what, a table_source can be a joined_table (as well as a bunch of other things). This means that the syntax:
A join B join C ON <condition1> ON <condition2>
Fits the grammar above and is interpreted as:
A join (B join C on <condition1>) ON <condition2>
That is, the expression B join C on <condition1> is treated as a "table_source".
My guess is that if both SQL Server and MySQL do it this way, then it is probably part of the standard. However, the standard is a bit harder to understand than SQL Server's syntax diagrams.
I think this is down to the way right outer join is evaluated:
Select
*
from
a
right outer join
b
inner join
c
on b.id = c.id
on a.id = b.id;
is evaluated as
Select
*
from
a
right outer join (
b
inner join
c
on b.id = c.id
)
on a.id = b.id;
Mixing left and right outer join is a path to madness.
The OP's query seems to be driven by a desire to exclude parenthesis, or derived tables. It is equivalent to:
Select
a.id ida,
b.id idb,
c.id idc,
d.id idd,
e.id ide
From
D
left outer join
A
on d.id = a.id
left outer join
E
on d.id = e.id
left outer join (
B
inner join
C
on b.id = c.id
)
on d.id = b.id;
Example SQLFiddle
I have the following schema.
I can run two queries fairly simply
select * from booking_model_assignment
join booking_model on booking_model_assignment.booking_model_id = booking_model.id
left outer join axis_channel_mappings on bmi_id = axis_channel_mappings.assignment_id
left outer join axis_revenue_stream_mappings on bmi_id = axis_revenue_stream_mappings.assignment_id
which will give me all of the combinations of channel mappings and 'revenue_stream_mappings' which fit a booking model, with Null if there is one which only matches in one of the tables.
The other query
select * from axis_channel join axis_revenue_stream
Gives all of the possible combinations of channels and revenue streams.
What I would like is a query which will give all of the combinations, and the booking_model if that combination matches.
Any time I try to join or subquery I seem to get too many, or too few results. I think the issue is that I want the assignment_id to match across outer joins but only if there is an outer join.
The schema is laid out like this so it will be possible to add new axis and fit models to combinations, so if there is an easier way to achieve this I would be open to changing the schema.
EDIT
I have a partial solution based on Eggyal's answer but it is not extendable.
SELECT c.*, r.*, GROUP_CONCAT(a.bmi_id), GROUP_CONCAT(b.name) AS booking_models
FROM axis_channel c
CROSS JOIN axis_revenue_stream r
LEFT JOIN axis_channel_mappings cm ON cm.channel_id = c.id
LEFT JOIN axis_revenue_stream_mappings rm ON rm.revenue_stream_id = r.id
LEFT JOIN booking_model_assignment a ON (a.bmi_id = cm.assignment_id
AND a.bmi_id = rm.assignment_id)
OR (a.bmi_id = cm.assignment_id
AND rm.assignment_id IS NULL)
OR (cm.assignment_id IS NULL
AND a.bmi_id = cm.assignment_id)
LEFT JOIN booking_model b ON b.id = a.booking_model_id
GROUP BY c.id, r.id
But if I were to add more axes this query would grow way to cumbersome.
SELECT c.*, r.*, GROUP_CONCAT(b.name) AS booking_models
FROM axis_channel c
CROSS JOIN axis_revenue_stream r
LEFT JOIN axis_channel_mappings cm ON cm.channel_id = c.id
LEFT JOIN axis_revenue_stream_mappings rm ON rm.revenue_stream_id = r.id
LEFT JOIN booking_model_assignment a ON a.bmi_id = cm.assignment_id
AND a.bmi_id = rm.assignment_id
LEFT JOIN booking_model b ON b.id = a.booking_model_id
GROUP BY c.id, r.id
I have a mysql query as stated below, it returns exactly the results I want for one row, but doesn't return any other rows where I expect there to be 8 in my test data (there are 8 unique test ids). I was inspired by this answer but obviously messed up my implementation, does anyone see where I'm going wrong?
SELECT
c.first_name,
c.last_name,
n.test_name,
e.doc_name,
e.email,
e.lab_id,
a.test_id,
a.date_req,
a.date_approved,
a.accepts_terms,
a.res_value,
a.reason,
a.test_type,
a.date_collected,
a.date_received,
k.kind_name,
sum(case when metabolite_name = "Creatinine" then t.res_val end) as Creatinine,
sum(case when metabolite_name = "Glucose" then t.res_val end) as Glucose,
sum(case when metabolite_name = "pH" then t.res_val end) as pH
FROM test_requisitions AS a
INNER JOIN personal_info AS c ON (a.user_id = c.user_id)
INNER JOIN test_types AS d ON (a.test_type = d.test_type)
INNER JOIN kinds AS k ON (k.id = d.kind_id)
INNER JOIN test_names AS n ON (d.name_id = n.id)
INNER JOIN docs AS e ON (a.doc_id = e.id)
INNER JOIN test_metabolites AS t ON (t.test_id = a.test_id)
RIGHT JOIN metabolites AS m ON (m.id = t.metabolite_id)
GROUP BY a.test_id
ORDER BY (a.date_approved IS NOT NULL),(a.res_value IS NOT NULL), a.date_req, c.last_name ASC;
Most of your joins are inner joins. The last is a right outer join. As written, the query keeps all the metabolites, but not necessarily all the tests.
I would suggest that you change them all to left outer joins, because you want to keep all the rows in the first table:
FROM test_requisitions AS a
LEFT JOIN personal_info AS c ON (a.user_id = c.user_id)
LEFT JOIN test_types AS d ON (a.test_type = d.test_type)
LEFT JOIN kinds AS k ON (k.id = d.kind_id)
LEFT JOIN test_names AS n ON (d.name_id = n.id)
LEFT JOIN docs AS e ON (a.doc_id = e.id)
LEFT JOIN test_metabolites AS t ON (t.test_id = a.test_id)
LEFT JOIN metabolites AS m ON (m.id = t.metabolite_id)
I would also suggest that your aliases be related to the table, so tr for test_requisition, pi for personal_info, and so on.
Been coding for 48 hours straight and am banging my head against the wall here. Please help me with this small issue.
My SQL query is this:
SELECT u.Firstname, u.Lastname, u.Rep, u.Email, u.Password, u.Gender, u.Level,
u.Birthday, u.Achievements, u.Height, u.Unit, u.cityid, u.countryid,
r.RegDate, ci.Name AS City, co.Name AS Country
FROM Users u, Registry r, Cities ci, Countries co
WHERE u.id = 1 AND r.uid = u.id AND u.cityid = ci.id AND u.countryid = co.id
LIMIT 1
My problem is that I just noticed that sometimes Users.cityid and Users.countryid are NULL (which is OK).
I want the query to give me all the other info (like, return NULL for City and Country) for this user even if one or both those fields are NULL. How to make the AND-parts conditional?
I hope I'm making myself clear in my fogginess.
I think you need a couple of OUTER joins if I have understood your situation correctly.
SELECT ...
FROM Users u
INNER JOIN Registry r ON r.uid = u.id
LEFT JOIN Cities ci ON u.cityid = ci.id
LEFT JOIN Countries co ON u.countryid = co.id
WHERE u.id = 1
You need to use a LEFT JOIN on your tables instead of using a WHERE.
So your FROM turns into:
FROM Users u JOIN Registry r on u.id = r.uid
LEFT JOIN Cities ci ON u.cityid = ci.id
LEFT JOIN Countries co ON u.countryid = co.id
WHERE u.id = 1 LIMIT 1
The LEFT JOIN is an OUTER join; it will join across the tables where the leftmost (hence the LEFT in the JOIN) term in the JOIN (i.e., the first one that appears) has an entry but the other table does not. OUTER JOINs are useful for these situations where you don't necessarily have data entries in all the tables for what you want from a query; they can be confusing at first, but they become very important to using SQL well.