Given tables Contracts, Contract_Plans and Contract_Plan_Tags, can I select specific fields from all three within an inner join query?
Currently, I have
SELECT * FROM Contracts
INNER JOIN Contract_Plans
ON Contracts.ContractNum = Contract_Plans.ContractNum
INNER JOIN Contract_Plan_Tags
ON Contracts.ContractNum = Contract_Plan_Tags.ContractNum
WHERE Contract_Plan_Tags.tag_id = 44 OR Contract_Plan_Tags.tag_id = 45
This query returns all the fields, but is there any way to select the specific fields from the join tables?
I know I can do
SELECT ContractNum, ContractName FROM Contracts
...
...
but that only selects fields from Contracts, not the other tables.
You should learn about table aliases. The best table aliases are short abbreviations of the tables. You can then list the fields with the aliases:
SELECT c.ContractNum, c.ContractName, cpt.tag_id, . . .
FROM Contracts c
INNER JOIN Contract_Plans cp
ON c.ContractNum = cp.ContractNum
INNER JOIN Contract_Plan_Tags cpt
ON c.ContractNum = cpt.ContractNum
WHERE cpt.tag_id in (44, 45)
I notice that you use table names in the where clause, so I hope I understand the question correctly. You can use aliases (or table names) in the select and where (and group by and having) clauses.
You must prefix the fields by their table name if order for MySQL to distinguish them:
SELECT Contracts.This, Contract_Plan_Tags.That, Contract_Plans.There FROM Contracts
INNER JOIN Contract_Plans
ON Contracts.ContractNum = Contract_Plans.ContractNum
INNER JOIN Contract_Plan_Tags
ON Contracts.ContractNum = Contract_Plan_Tags.ContractNum
WHERE Contract_Plan_Tags.tag_id = 44 OR Contract_Plan_Tags.tag_id = 45
Yes, you can select any field from any table from the FROM clause. If two fields of two tables have the same name, then you must prefix the field with the name of the table (or else you get an error from the parser: "field name is ambiguous").
You may prefix the unambiguous field names too if you find it more readable.
In fact it works exactly the same way as in the WHERE clause.
Incidentally, the same requirement exists if you join tables from several databases. If two tables have the same name, you must prefix their name with the database name, but do not need to if the name is unambiguous.
Oh and you can also do this: SELECT table1.*, table2.some_field, table3.* FROM...
Related
I get a Syntax error: missing 'closing parenthesis' when I add a specifier to my USING statement
Here is an example similar to what I am doing:
SELECT * FROM student s JOIN test_result using (s.id) where s.id = 1335;
My actual query is much more complex, I have multiple tables that I want to join and some tables have columns with the same name so when I try to JOIN one of those tables with a third table, I get a Error Code: 1052. Column 's.id' in from clause is ambiguous. When I try to avoid that by specifying which table's id I want to use I get a Syntax error: missing 'closing parenthesis'
The column(s) within the USING() clause do not take aliases - the base idea is that this column should be available in both tables.
This also has the advantage of disambiguating the column, so you don't event need to prefix it with a table alias in the WHERE claus.
Just:
SELECT *
FROM student s
INNER JOIN test_result ts USING (id)
WHERE id = 1335;
The downside is that, if you use that in a multi-join context where several tables have an id column, you don't get to choose from which table id is picked. This can be troublesome in some situations. In that case, you need to switch back to the join ... on ... syntax:
SELECT *
FROM student s
INNER JOIN test_result ts on ts.id = s.id
WHERE id = 1335;
Reading your comment, I think you need an alias based join
SELECT *
FROM student s
JOIN test_result t ON t.id = s.id
where s.id = 1335;
Person is a table with columns PersonId, FirstName, LastName
Address is a table with columns PersonId, City, State
SELECT a.FirstName, a.LastName, b.City, b.State
FROM Person a, Address b
WHERE a.PersonId = b.PersonId;
I have two questions.
Please correct me if I'm wrong, but I'm guessing that the purpose of the (a., b.) extensions are used to indicate a specific column of an SQL table so there exists no ambiguity between selecting a column given two tables may have the same column name?
Is there a name for this?
These are called table aliases, and indeed their purpose is to indicate from which table each column comes from. This is mandatory to avoid ambiguity when a column by the same name exists in both tables - but also a general good practice, so people reading the query can understand it without having knowledge of the underlying table structures.
Note that you don’t necessarily need explicit aliases; you can also prefix the columns with the full table name if you like (like Person.FirstName): aliases just makes things shorter to write.
You should use meaningful aliases so it is easier to remember them through the query (Person would be best aliases p than a).
Finally, you should be using explicit, modern joins (with the on keyword) rather than old-school, implicit joins, whose syntax is not state of the art since ANSI SQL 92, decades ago.
Your query:
SELECT p.FirstName, p.LastName, a.City, a.State
FROM Person p
INNER JOIN Address a ON a.PersonId = p.PersonId;
Generally it's called an identifier qualifier:
https://dev.mysql.com/doc/refman/8.0/en/identifier-qualifiers.html
...a column name may be given a table-name qualifier, which itself may
be given a database-name qualifier. Examples of unqualified and
qualified column references in SELECT statements:
SELECT c1 FROM mytable
WHERE c2 > 100;
SELECT mytable.c1 FROM mytable
WHERE mytable.c2 > 100;
SELECT mydb.mytable.c1 FROM mydb.mytable
WHERE mydb.mytable.c2 > 100;
You can use a table alias as a qualifier either to make it shorter than the full table name, or because you are doing a self-join and you use aliases to make more than one reference to the same table name.
SELECT c1, c2, t1.c FROM db1.t AS t1 INNER JOIN db2.t AS t2
WHERE t2.c > 100;
2. Is there a name for this?
It's called giving identifier. In SELECT city.Name FROM city; city is the identifier to Name, in SELECT c.Name FROM city c; however alias c is the identifier to Name.
1. Please correct me if I'm wrong, but I'm guessing that the purpose
of
the (a., b.) extensions are used to indicate a specific column of an
SQL table so there exists no ambiguity between selecting a column
given two tables may have the same column name?
This is true apart from the aliases. One can achieve this without aliases by referencing the exact names of the tables.
Aliases help,
Changing the related table name in one place and in one place alone
e.g.
changing sales to stock in
SELECT *
FROM sales s
WHERE s.col1 ... s.col2 ... s.col3 ...
-- WHERE sales.col1 ... sales.col2 ... sales.col3 ...
is relatively easier.
Simplify the details of the query by shortening the columns refered
e.g.
It's harder to understand the query below than the one where it has
simple aliases:
SELECT
product_color_configuration.Name
FROM product_color_configuration
LEFT JOIN product_channel_permission ON product_color_configuration.ProductId = product_channel_permission.ProductId ...
Remove the ambiguity when the same table is referenced multiple
times distinctly:
SELECT
COALESCE(c1.Name, c2.Name) -- if Id exists prioritize
FROM table t
LEFT JOIN city c1 ON t.CityId = c1.Id
LEFT JOIN city c2 ON c2.Name REGEXP t.CityGuessedName
;
I’ve got quite a few tables with product information. The columns on each table that I’m pulling from in this particular query have the exact same column names. I’ve been attempting to do it via a UNION ALL but for some reason it is throwing an error saying non-object but all the column names are correct.
I’m using a format that I found online. But obviously something is wrong. There are more tables; however, this is how it starts (with 2). I’d prefer not to have to code each select statement in the union with unique table abbreviations if I don’t have to.
I don’t have to use union if there is a better method.
All tables share data on Product_Categories and Product_Sub_Category.
The only thing unique to each table is id and part_number.
SELECT f.id,f.part_number,f.cat,f.subcat,f.table_name FROM
(
SELECT t.id,t.part_number,psc.name as subcat,c.name as cat, c.table_name FROM Steel_Strapping as t JOIN Product_Sub_Category as psc ON t.subcat = psc.id JOIN Product_Categories as c ON psc.category = c.id ORDER BY c.sort_order,psc.sort_order,t.sort_order
UNION ALL
SELECT t.id,t.part_number,psc.name as subcat,c.name as cat, c.table_name FROM Product as t JOIN Product_Sub_Category as psc ON t.subcat = psc.id JOIN Product_Categories as c ON psc.category = c.id ORDER BY c.sort_order,psc.sort_order,t.sort_order
) f
My end result is one full list of all products sharing column names. Ex: $result[‘part_number’] will pull part numbers from ALL tables listed in union.
I found the solution when playing around with code. I had to add parenthesis (select...) UNION JOIN (select...) inside the parent select statement
I have three tables
I would like to request all persons, where the companyTypeID is for example '2'. How can I query that?
You could use the in operator:
SELECT *
FROM persons
WHERE CompanyId IN (SELECT CompanyId
FROM company
WHERE CompanyTypeId = 2)
Do an INNER JOIN (left or right joins are functionally similar, the only difference is which side of the equation is honoured). Nested queries / subqueries are extremely expensive if they become dependent in nature—even though that's not the scenario in your case—and I do not recommend using them for large tables.
SELECT t1.*
FROM Persons AS t1
LEFT JOIN Company AS t2 ON
t2.companyTypeID = t1.CompanyID
To ensure that you are using an index for joining, you should create indexes on the companyTypeID and CompanyID columns of each table. Prepend EXPLAIN EXTENDED to the query above to verify that the indexes are indeed being used.
You need to use SQL statement JOIN. It's all about mathematical sets!
A Graphic (and superficial) explanation about JOINs statement under mathematical sets approach:
https://www.google.com.br/imgres?imgurl=http%3A%2F%2Fi.imgur.com%2FhhRDO4d.png&imgrefurl=https%3A%2F%2Fwww.reddit.com%2Fr%2Fprogramming%2Fcomments%2F1xlqeu%2Fsql_joins_explained_xpost_rsql%2F&docid=q4Ank7XVw8j7DM&tbnid=f-L_7a3HkxW_3M%3A&w=1000&h=740&client=ubuntu&bih=878&biw=1745&ved=0ahUKEwj2oaer5evPAhUBFZAKHe4nAdAQMwgdKAEwAQ&iact=mrc&uact=8#h=740&w=1000
SQL for your problem(If I got this):
SELECT * FROM Persons p LEFT JOIN Company c ON c.ID = p.companyID
LEFT JOIN CompanyType ct ON ct.ID = c.companyTypeID
WHERE c.id = 2;
Why 'LEFT JOIN'? Checkout the the link about set approach explanation above!
The second 'LEFT JOIN' is just to bring the description of companyType table.
The "*" in statement is didatic! You must not use this in production for the good of performance. Therefore, you must to replace the '*' with all the fields you need. For example, "CONCAT(p.firstname,CONCAT(" ",p.lastname)) as PersonName, c.name CompanyName,..." or something like that. I suppose you're using MySQL
Hope I've helped!
Perform an SQL join:
Joins are quicker than the subquery, in the other post:
SELECT Persons.firstname AS first name
FROM Persons
JOIN Company ON company.ID == Persons.CompanyID
WHERE Company.companyTypeID == 2
Although you will need to select all he fields you want, using alias to simplify the names
Basically, there is an attribute table and translation table - many translations for one attribute.
I need to select id and value from translation for each attribute in a specified language, even if there is no translation record in that language. Either I am missing some join technique or join (without involving language table) is not working here since the following do not return attributes with non-existing translations in the specified language.
select a.attribute, at.id, at.translation
from attribute a left join attributeTranslation at on a.id=at.attribute
where al.language=1;
So I am using subqueries like this, problem here is making two subqueries to the same table with the same parameters (feels like performance drain unless MySQL groups those, which I doubt since it makes you do many similar subqueries)
select attribute,
(select id from attributeTranslation where attribute=a.id and language=1),
(select translation from attributeTranslation where attribute=a.id and language=1),
from attribute a;
I would like to be able to get id and translation from one query, so I concat columns and get the id from string later, which is at least making single subquery but still not looking right.
select attribute,
(select concat(id,';',title)
from offerAttribute_language
where offerAttribute=a.id and _language=1
)
from offerAttribute a
So the question part.
Is there a way to get multiple columns from a single subquery or should I use two subqueries (MySQL is smart enough to group them?) or is joining the following way to go:
[[attribute to language] to translation] (joining 3 tables seems like a worse performance than subquery).
Yes, you can do this. The knack you need is the concept that there are two ways of getting tables out of the table server. One way is ..
FROM TABLE A
The other way is
FROM (SELECT col as name1, col2 as name2 FROM ...) B
Notice that the select clause and the parentheses around it are a table, a virtual table.
So, using your second code example (I am guessing at the columns you are hoping to retrieve here):
SELECT a.attr, b.id, b.trans, b.lang
FROM attribute a
JOIN (
SELECT at.id AS id, at.translation AS trans, at.language AS lang, a.attribute
FROM attributeTranslation at
) b ON (a.id = b.attribute AND b.lang = 1)
Notice that your real table attribute is the first table in this join, and that this virtual table I've called b is the second table.
This technique comes in especially handy when the virtual table is a summary table of some kind. e.g.
SELECT a.attr, b.id, b.trans, b.lang, c.langcount
FROM attribute a
JOIN (
SELECT at.id AS id, at.translation AS trans, at.language AS lang, at.attribute
FROM attributeTranslation at
) b ON (a.id = b.attribute AND b.lang = 1)
JOIN (
SELECT count(*) AS langcount, at.attribute
FROM attributeTranslation at
GROUP BY at.attribute
) c ON (a.id = c.attribute)
See how that goes? You've generated a virtual table c containing two columns, joined it to the other two, used one of the columns for the ON clause, and returned the other as a column in your result set.