Just a small MySQL Statement example:
SELECT m.nameMarket nm, m.idCity idc, c.cityname cn
FROM markets m
LEFT JOIN cities c ON m.idCity = c.idCity
works as expected. Why does neither of both work here (using the alias in the Select-Clause in the Join-Statement):
SELECT m.nameMarket nm, m.idCity idc, c.cityname cn
FROM markets m
LEFT JOIN cities c ON idc = c.idCity
OR:
SELECT m.nameMarket nm, m.idCity idc, c.cityname cn
FROM markets m
LEFT JOIN cities c ON m.idc = c.idCity
Or can I never use a column-alias in the Join Statement? (Im confused because I can also use a table-alias in the statement as done in the first SQL-code).
And second question is: Is the sequence of the statement in Left Join relevant? I.e.:
SELECT m.nameMarket nm, m.idCity idc, c.cityname cn
FROM markets m
LEFT JOIN cities c ON m.idCity = c.idCity
SELECT m.nameMarket nm, m.idCity idc, c.cityname cn
FROM markets m
LEFT JOIN cities c ON c.idCity = m.idCity
I mean the result is the same, but is there any speed/performance issue or something else related to it?
Thanks
Why does neither of both work here (using the alias in the Select-Clause in the Join-Statement)?
As you've discovered, column aliases in the SELECT clause do not work in other clauses. In some RDBMSs they work in the ORDER BY clause, and less commonly in the WHERE clause, but most often they don't. The reason is simply because the database engine doesn't look at or assign aliases in the SELECT clause until very late in the query's execution. The only exception is in subqueries.
Is the sequence of the statement in Left Join relevant?
I mean the result is the same, but is there any speed/performance issue or something else related to it?
I would expect the database engine to parse the queries exactly the same. I would go so far as to say there is a bug in the database engine if it doesn't.
Fire up the MySQL query analyzer and take a look at the execution plans with the EXPLAIN command. They should be identical.
Think about when you select something and alias it. It doesn't exist until you select it (what if it's a subset of something for example). So, does it make sense to do a join on an alias that doesn't exist yet? It's basically the last thing that's applied to the entire statement as it's ran.
A table already exists - so it makes sense to reference that with an alias. Usually however, you have to make a separate reference to refer to that table again during the query. This prevents ambiguity if you end up doing multiple joins on the same table.
And does the order matter? Well, the query optimizer will try all sorts of permutations and choose the best one. So no, it shouldn't.
Related
I have a database with 22 tables most tables have a few column fields with identical names. When I use INNER JOIN on the tables with the duplicate column names it will ignore them in the last table. Right now I am only joining 3 tables
SELECT * FROM company C
INNER JOIN typeofdealer PI ON C.CompanyID = PI.CompanyID
INNER JOIN typeofservices TS ON PI.CompanyID = TS.CompanyID
So in this example typeofdealer and typeofservices both contain column names Other and OtherText When I get this query back I only see one Other and one OtherText.
I just discovered as I was writing this that if I do the query in "phpmyadmin" that I get it back exactly as intended. I am doing this query using prepared statements in php and outputting the results using:
echo "<PRE>";
print_r($results);
echo "</PRE>";
I believe the problem is that php arrays cannot contain duplicate fields so is there a way to circumvent the issue I am having?
The workaround you can do for this is to use an alias AS to make a distinction between columns with the same names. Something like:
SELECT PI.Other AS Other1, PI.OtherText AS OtherText2, TS.Other AS Other2, TS.OtherText AS OtherText2 FROM company C
INNER JOIN typeofdealer PI ON C.CompanyID = PI.CompanyID
INNER JOIN typeofservices TS ON PI.CompanyID = TS.CompanyID
Actually it will work the same way if you omit the AS keyword say SELECT PI.Other Other1, PI.OtherText OtherText2
The problem is, as you rightfully suggested, that PHP won't allow multiple entries into an array with the same key name.
The simplest way around this, is to alias the fields in the column selection section of the query, like so:
SELECT *, typeofservices.Other as ServicesOther, typeofservices.OtherText as ServicesOtherText FROM company C
INNER JOIN typeofdealer PI ON C.CompanyID = PI.CompanyID
INNER JOIN typeofservices TS ON PI.CompanyID = TS.CompanyID
It's not pretty but it's simple.
I'm having a problem with my SELECT sql statement and I haven't figured it out yet. When I print out the results using mysql_fetch_assoc() function, I get repetitive rows/records. A record is repeated 13 times. I don't know why and I have done it right as far as my knowledge tells me.
The following is my sql query:
SELECT
members.member_id,
members.firstname,
members.lastname,
billing_details.Street_Address,
billing_details.Mobile_No,
orders_details.*,
food_details.*,
categories.*,
cart_details.*,
quantities.*
FROM
members, billing_details, orders_details, categories,
quantities, food_details, cart_details
WHERE
members.member_id=orders_details.member_id AND
billing_details.billing_id=orders_details.billing_id AND
orders_details.cart_id=cart_details.cart_id AND
cart_details.food_id=food_details.food_id AND
cart_details.quantity_id=quantities.quantity_id
You don't have "categories" in your WHERE clause. I am guessing you have 13 categories? If you need a better explanation, let me know.
Use SELECT DISTINCT.
Please Note
Using the mysql_* libraries is bad practise. They are Deprecated and should be replaced by either the mysqli_* libraries or a PDO object.
Why dont you make left Joins like below
SELECT members.member_id, members.firstname, members.lastname, billing_details.Street_Address, billing_details.Mobile_No, orders_details.*, food_details.*, categories.*, cart_details.*, quantities.* FROM members as m
Left join billing_details as b ON b.billing_id=m.?
LEFT JOIN orders_details as o ON o.cart_id=m.?
LEFT JOIN food_details as f ON f.f_id =m.?
LEFT JOIN cart_details as c ON c.?=?
LEFT JOIN quantitiesas q ON q.?=?
LEFT JOIN categories as cat ON cat.?=?
If you only want each member_id to appear once, you can use DISTINCT:
SELECT DISTINCT members.member_id, ...
You can also use JOIN USING to avoid repetition (DRY):
http://dev.mysql.com/doc/refman/5.7/en/join.html (search for USING(column_list))
That way, you'd also have noticed the msising predicate, since it would be right next to the joined table name.
Can you let me know if my interpretation is correct (the last AND part)?
$q = "SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND id IN (
SELECT registrar_id
FROM registrations_industry
WHERE industry_id = '$industryid'
)";
Below was really where I am not sure:
... AND id IN (select registrar_id from registrations_industry where industry_id='$industryid')
Interpretation: Get any match on id(registrations id field) equals registrar_id(field) from the join table registrations_industry where industry_id equals the set $industryid
Is this select statement considered a sub routine since it's a query within the main query?
So an example would be with the register table id search to 23 would look like:
registrations(table)
id=23,title=owner,name=mike,company=nono,address1=1234 s walker lane,address2
registrations_industry(table)
id=256, registrar_id=23, industry_id=400<br>
id=159, registrar_id=23, industry_id=284<br>
id=227, registrar_id=23, industry_id=357
I assume this would return 3 records with the same registration table data And of course varying registrations_industry returns.
For a given test data set your query will return one record. This one:
id=23,title=owner,name=mike,company=nono,address1=1234 s walker lane,address2
To get three records with the same registration table data and varying registrations_industry you need to use JOIN.
Something like this:
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations AS r
LEFT OUTER JOIN registrations_industry AS ri
ON ri.registrar_id=r.id
WHERE r.title!=0 AND ri.industry_id={$industry_id}
Sorry for the essay, I didn't realize it was as long as it is until looking at it now. And although you've checked an answer, I hope you read this gain some insight into why this solution is preferred and how it evolved out of your original query.
First things first
Your query
$q = "SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND id IN (
SELECT registrar_id
FROM registrations_industry
WHERE industry_id = '$industryid'
)";
seems fine. The IN syntax is equivalent to a number of OR matches. For example
WHERE field_id IN (101,102,103,105)
is functionally equivalent to
WHERE (field_id = 101
OR field_id = 102
OR field_id = 103
OR field_id = 105)
You complicate it a bit by introducing a subquery, no problem. As long as your subquery returns one column (and yours does), passing it to IN will be fine.
In your case, you're comparing registrations.id to registrations_industry.registrar_id. (Note: This is just <table>.<field> syntax, nothing special, but helpful to disambiguate what tables your fields are in.)
This seems fine.
What happens
SQL would first run the subquery, generating a result set of registrar_ids where the industry_id was set as specified.
SQL would then run the outer query, replacing the subquery with its results and you would get rows from registrations where registrations.id matched one of the registrar_ids returned from the subquery.
Subqueries are helpful to debug your code, because you can pull out the subquery and run it separately, ensuring its output is as you expect.
Optimization
While subqueries are good for debugging, they're slow, at least slower than using optmized JOIN statements.
And in this case, you can convert your query to a single-level query (without subqueries) by using a JOIN.
First, you'd start with basically the exact same outer query:
SELECT title,name,company,address1,address2
FROM registrations
WHERE title != 0 AND ...
But you're also interested in data from the registrations_industry table, so you need to include that. Giving us
SELECT title,name,company,address1,address2
FROM registrations, registrations_industry
WHERE title != 0 AND ...
We need to fix the ... and now that we have the registrations_industry table we can:
SELECT title,name,company,address1,address2
FROM registrations, registrations_industry
WHERE title != 0
AND id = registrar_id
AND industry_id = '$industryid'
Now a problem might arise if both tables have an id column -- since just saying id is ambiguous. We can disambiguate this by using the <table>.<field> syntax. As in
SELECT registrations.title, registrations.name,
registrations.company, registrations.address1, registrations.address2
FROM registrations, registrations_industry
WHERE registrations.title != 0
AND registrations_industry.industry_id = '$industryid'
We didn't have to use this syntax for all the field references, but we chose to for clarity. The query now is unnecessarily complex because of all the table names. We can shorten them while still providing disambiguation and clarity. We do this by creating table aliases.
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND ri.industry_id = '$industryid'
By placing r and ri after the two tables in the FROM clause, we're able to refer to them using these shortcuts. This cleans up the query but still gives us the ability to clearly specify which tables the fields are coming from.
Sidenote: We could be more explicit about the table aliases by including the optional AS e.g. FROM registrationsASr rather than just FROM registrations r, but I typically reserve AS for field aliases.
If you run the query now you will get what is called a "Cartesian product" or in SQL lingo, a CROSS JOIN. This is because we didn't define any relationship between the two tables when, in fact, there is one. To fix this we need to reintroduce part of the original query that was lost: the relationship between the two tables
r.id = ri.registrar_id
so that our query now looks like
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND r.id = ri.registrar_id
AND ri.industry_id = '$industryid'
And this should work perfectly.
Nitpicking -- implicit vs. explicit joins
But the nitpicker in me needs to point out that this is called an "implicit join". Basically you're joining tables but not using the JOIN syntax.
A simpler example of an implicit join is
SELECT *
FROM foo f, bar b
WHERE f.id = b.foo_id
The corresponding explicit syntax is
SELECT *
FROM foo f
JOIN bar b ON f.id = b.foo_id
The result will be identical but it is using proper (and clearer) syntax. (Its clearer because it explicitly stats that there is a relationship between the foo and bar tables and it is defined by f.id = b.foo_id.)
We could similarly express your implicit query
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r, registrations_industry ri
WHERE r.title != 0
AND r.id = ri.registrar_id
AND ri.industry_id = '$industryid'
explicitly as follows
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r
JOIN registrations_industry ri ON r.id = ri.registrar_id
WHERE r.title != 0
AND ri.industry_id = '$industryid'
As you can see, the relationship between the tables is now in the JOIN clause, so that the WHERE and subsequent AND and OR clauses are free to express any restrictions. Another way to look at this is if you took out the WHERE + AND/OR clauses, the relationship between tables would still hold and the results would still "make sense" whereas if you used the implicit method and removed the WHERE + AND/OR clauses, your result set would contain rows that were misleading.
Lastly, the JOIN syntax by itself will cause rows that are in registrations, but do not have any corresponding rows in registrations_industry to not be returned.
Depending on your use case, you may want rows from registrations to appear in the results even if there are no corresponding entries in registrations_industry. To do this you would use what's called an OUTER JOIN. In this case, we want what is called a LEFT OUTER JOIN because we want all of the rows of the table on the left (registrations). We could have alternatively used RIGHT OUTER JOIN for the right table or simply OUTER JOIN for the outer join of both tables.
Therefore our query becomes
SELECT r.title, r.name, r.company, r.address1, r.address2
FROM registrations r
LEFT OUTER JOIN registrations_industry ri ON r.id = ri.registrar_id
WHERE r.title != 0
AND ri.industry_id = '$industryid'
And we're done.
The end result is we have a query that is
faster in terms of runtime
more compact / concise
more explicit about what tables the fields are coming from
more explicit about the relationship between the tables
A simpler version of this query would be:
SELECT title, name, company, address1, address2
FROM registrations, registrations_industry
WHERE title != 0
AND id = registrar_id
AND industry_id = '$industryid'
Your version was a subquery, this version is a simple join. Your assumptions about your query are generally correct, but it is harder for SQL to optimize and a little harder to unravel to anyone trying to read the code. Also, you won't be able to extract the data from the registrations_industry table in that parent SELECT statement because it's not technically joining and the subtable is not a part of the parent query.
I have a query I need to run which is currently running extremely slow. The table I am working with has nearly 1 million records.
What I need to do is search for items with a LIKE name : Example "SELECT name, other, stuff FROM my_table WHERE name LIKE '%some_name%'"
In addition it has to be able to exclude certain terms with wildcards from the search results so it turns into something like this :
SELECT name, other, stuff
FROM my_table
WHERE name LIKE '%some_name%'
AND name NOT IN (SELECT name FROM my_table WHERE name LIKE '%term1%' AND name LIKE '%term2%' AND name LIKE '%term3%')
To top things off, I have two INNER JOINs and I have to execute it in PHP. I have indexes on the table for relevant columns. It is on a server that is fast for pretty much every other query I can throw at it.
The question is, is there a different method I could be using to do this type of query thats is quick?
UPDATES
This is my actual query after adding in FULLTEXT index to the chem table and trying the match function instead. NOTE : I don't have control over table/column names. I know they dont look nice.
CREATE FULLTEXT INDEX name_index ON chems (name)
SELECT f.name fname, f.state, f.city, f.id fid,
cl.alt_facilid_ida, cl.alt_facili_idb,
c.ave, c.name, c.id chem_id,
cc.first_name, cc.last_name, cc.id cid, cc.phone_work
FROM facilities f
INNER JOIN facilitlt_chemicals_c cl ON (f.id = cl.alt_facilid485ilities_ida)
INNER JOIN chems c ON (cl.alt_facili3998emicals_idb = c.id)
LEFT OUTER JOIN facilities_contacts_c con ON (f.id = con.alt_facili_ida)
LEFT OUTER JOIN contacts cc ON (con.alt_facili_idb = cc.id)
WHERE match(c.name) against ('+lead -gas' IN BOOLEAN MODE)
That is just an example of a simple query. The actual terms could be numerous.
You want to implement MySQL's full text capabilities on the columns you're searching. Read more about it here.
Moreover, you probably want to do a boolean search:
select
t1.name,
t1.stuff
from
my_table t1
where
match(t1.name) against ('+some_name -term1 -term2 -term3' in boolean mode)
I would suggest to use external Full-text engine like Lucene or Sphinx. Especially if you plan to fire search queries against relatively big datasets.
In Sphinx case you can use SELECT-like syntax called SphinxQL which will do exactly as required:
SELECT * FROM <sphinx_index_name> WHERE MATCH('some_name -term1 -term2 -term3');
as described in http://sphinxsearch.com/docs/current.html#extended-syntax (NOT operator in your case). Plus you could enable morphology support which might be helpful as well.
You can combine MySQL and Sphinx queries with http://sphinxsearch.com/docs/current.html#sphinxse
I just realized that I'm going to have to start aliasing my database calls due to repeating column names in my join tables. Is there a way to automatically tell SQL to alias all my column names so that they are returned with a prefix of the table name? Otherwise it appears to be quite confusing when only some of them are aliased. Just trying to be consistent without writing tons of extra code.
$sql = "SELECT contracts.po_number, contracts.start_date, contracts.end_date, contracts.description, contracts.taa_required, contracts.account_overdue, jobs.id AS jobs_id, jobs.job_number, companies.id AS companies_id, companies.name AS companies_name
FROM contracts
LEFT JOIN jobs ON contracts.job_id = jobs.id
LEFT JOIN companies ON contracts.company_id = companies.id
WHERE contracts.id = '$id'
ORDER BY contracts.end_date";
No, but you can make life a little easier by using table aliases:
SELECT c.po_number, c.start_date, c.end_date, c.description,
c.taa_required, c.account_overdue, j.id AS jobs_id, j.job_number,
cm.id AS companies_id, cm.name AS companies_name
FROM contracts c
LEFT JOIN jobs j ON c.job_id = j.id
LEFT JOIN companies cm ON c.company_id = cm.id
WHERE c.id = '$id'
ORDER BY c.end_date
you can use alias tables in your sql statements so you have to write less, but to actually access the columns from php there's no way around aliasing all of them, if you want to access them by name.
you can also access columns with indexes from php, but that's a maintenance nightmare
I would recommend to always alias table names. It makes it very hard to read later, if you skip alias.
For info, theres a gotcha in MySQL 5.6 (possibly others!)
SELECT *
FROM table1
LEFT JOIN table2 ON PKI = FKI
works as expected.. but recently I mispelt 'LEFT' as 'LEFY' in a query and it also worked but with a standard join! so therefore
SELECT *
FROM table1
LEFY JOIN table2 ON PKI = FKI
also works just fine as does any substitute for the word LEFY, so beware a typo changing your query !!