Flu*_*feh 425 mysql sql select
我想知道以下内容:
我打算在我的(例如 - PHP)应用程序中使用它,但不想对数据库运行多个查询,我有什么选项可以从单个查询中的多个表中获取数据?
注意:我正在写这篇文章,因为我希望能够链接到我在PHP队列中经常遇到的众多问题的精心编写的指南,因此我可以在发布答案时链接到此以获取更多详细信息.
答案涵盖以下内容:
Flu*_*feh 455
这个答案包括:
有许多方法可以从数据库中的多个表中检索数据.在这个答案中,我将使用ANSI-92连接语法.这可能与许多使用旧版ANSI-89语法的其他教程不同(如果你习惯于89,可能看起来不那么直观 - 但我只能说是尝试它)因为它更容易了解查询何时开始变得更复杂.为什么要用它?是否有性能提升?在简短的回答是否定的,但它是更易于阅读,一旦你习惯了它.使用此语法更容易读取其他人编写的查询.
我还将使用一个小型caryard的概念,它有一个数据库来跟踪它有哪些可用的汽车.所有者已雇用您作为他的IT计算机人员,并希望您能够将他所要求的数据丢给他.
我已经制作了一些将由最终表使用的查找表.这将为我们提供一个合理的模型.首先,我将针对具有以下结构的示例数据库运行查询.我将尝试思考在开始时所犯的常见错误,并解释它们出了什么问题 - 当然还要说明如何纠正错误.
第一个表格只是一个颜色列表,以便我们知道我们在车场里有什么颜色.
mysql> create table colors(id int(3) not null auto_increment primary key,
-> color varchar(15), paint varchar(10));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| color | varchar(15) | YES | | NULL | |
| paint | varchar(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
mysql> insert into colors (color, paint) values ('Red', 'Metallic'),
-> ('Green', 'Gloss'), ('Blue', 'Metallic'),
-> ('White' 'Gloss'), ('Black' 'Gloss');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from colors;
+----+-------+----------+
| id | color | paint |
+----+-------+----------+
| 1 | Red | Metallic |
| 2 | Green | Gloss |
| 3 | Blue | Metallic |
| 4 | White | Gloss |
| 5 | Black | Gloss |
+----+-------+----------+
5 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
品牌表标识了caryard可能出售的汽车的不同品牌.
mysql> create table brands (id int(3) not null auto_increment primary key,
-> brand varchar(15));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from brands;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| brand | varchar(15) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)
mysql> insert into brands (brand) values ('Ford'), ('Toyota'),
-> ('Nissan'), ('Smart'), ('BMW');
Query OK, 5 rows affected (0.00 sec)
Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from brands;
+----+--------+
| id | brand |
+----+--------+
| 1 | Ford |
| 2 | Toyota |
| 3 | Nissan |
| 4 | Smart |
| 5 | BMW |
+----+--------+
5 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
模型表将涵盖不同类型的汽车,使用不同的汽车类型而不是实际的汽车模型会更简单.
mysql> create table models (id int(3) not null auto_increment primary key,
-> model varchar(15));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from models;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| model | varchar(15) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> insert into models (model) values ('Sports'), ('Sedan'), ('4WD'), ('Luxury');
Query OK, 4 rows affected (0.00 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from models;
+----+--------+
| id | model |
+----+--------+
| 1 | Sports |
| 2 | Sedan |
| 3 | 4WD |
| 4 | Luxury |
+----+--------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
最后,要将所有这些其他表格捆绑在一起,将所有这些表格联系在一起.ID字段实际上是用于识别汽车的唯一批号.
mysql> create table cars (id int(3) not null auto_increment primary key,
-> color int(3), brand int(3), model int(3));
Query OK, 0 rows affected (0.01 sec)
mysql> show columns from cars;
+-------+--------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| color | int(3) | YES | | NULL | |
| brand | int(3) | YES | | NULL | |
| model | int(3) | YES | | NULL | |
+-------+--------+------+-----+---------+----------------+
4 rows in set (0.00 sec)
mysql> insert into cars (color, brand, model) values (1,2,1), (3,1,2), (5,3,1),
-> (4,4,2), (2,2,3), (3,5,4), (4,1,3), (2,2,1), (5,2,3), (4,5,1);
Query OK, 10 rows affected (0.00 sec)
Records: 10 Duplicates: 0 Warnings: 0
mysql> select * from cars;
+----+-------+-------+-------+
| id | color | brand | model |
+----+-------+-------+-------+
| 1 | 1 | 2 | 1 |
| 2 | 3 | 1 | 2 |
| 3 | 5 | 3 | 1 |
| 4 | 4 | 4 | 2 |
| 5 | 2 | 2 | 3 |
| 6 | 3 | 5 | 4 |
| 7 | 4 | 1 | 3 |
| 8 | 2 | 2 | 1 |
| 9 | 5 | 2 | 3 |
| 10 | 4 | 5 | 1 |
+----+-------+-------+-------+
10 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
这将为我们提供足够的数据(我希望),以覆盖不同类型的连接的下面的示例,并提供足够的数据,使它们值得.
因此,老板想知道他拥有的所有跑车的ID.
这是一个简单的两个表连接.我们有一个表格,用于识别模型和包含可用库存的表格.正如你所看到的,在数据model的列cars表涉及models的列cars,我们有表.现在,我们知道models表的ID为1for,Sports所以让我们编写连接.
select
ID,
model
from
cars
join models
on model=ID
Run Code Online (Sandbox Code Playgroud)
那么这个查询看起来不错吧?我们已经确定了两个表并包含了我们需要的信息,并使用了一个正确标识要加入的列的连接.
ERROR 1052 (23000): Column 'ID' in field list is ambiguous
Run Code Online (Sandbox Code Playgroud)
哦,不!我们的第一个查询出错了!是的,这是一个梅花.你看,查询确实得到了正确的列,但是其中一些列存在于两个表中,因此数据库对我们所指的实际列和位置感到困惑.有两种解决方案可以解决这个问题.第一个是好的和简单的,我们可以用来tableName.columnName准确地告诉数据库我们的意思,像这样:
select
cars.ID,
models.model
from
cars
join models
on cars.model=models.ID
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 3 | Sports |
| 8 | Sports |
| 10 | Sports |
| 2 | Sedan |
| 4 | Sedan |
| 5 | 4WD |
| 7 | 4WD |
| 9 | 4WD |
| 6 | Luxury |
+----+--------+
10 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
另一种可能更常用,称为表别名.这个例子中的表有简短的简单名称,但输入类似的东西KPI_DAILY_SALES_BY_DEPARTMENT可能会很快变老,所以一个简单的方法就是像这样昵称表:
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
Run Code Online (Sandbox Code Playgroud)
现在,回到请求.正如您所看到的,我们拥有所需的信息,但我们也有未被要求的信息,因此我们需要在声明中包含where子句,以便按照要求获得跑车.由于我更喜欢表别名方法而不是一遍又一遍地使用表名,所以从这一点开始我将坚持使用它.
显然,我们需要在查询中添加一个where子句.我们可以通过ID=1或识别跑车model='Sports'.由于ID已被索引并且主键(并且它恰好是较少键入),因此我们在查询中使用它.
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
where
b.ID=1
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 3 | Sports |
| 8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
答对了!老板很高兴.当然,作为老板,从不满足于他的要求,他会查看信息,然后说我也想要颜色.
Okay, so we have a good part of our query already written, but we need to use a third table which is colors. Now, our main information table cars stores the car color ID and this links back to the colors ID column. So, in a similar manner to the original, we can join a third table:
select
a.ID,
b.model
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
where
b.ID=1
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 3 | Sports |
| 8 | Sports |
| 10 | Sports |
+----+--------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
Damn, although the table was correctly joined and the related columns were linked, we forgot to pull in the actual information from the new table that we just linked.
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
where
b.ID=1
+----+--------+-------+
| ID | model | color |
+----+--------+-------+
| 1 | Sports | Red |
| 8 | Sports | Green |
| 10 | Sports | White |
| 3 | Sports | Black |
+----+--------+-------+
4 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
Right, that's the boss off our back for a moment. Now, to explain some of this in a little more detail. As you can see, the from clause in our statement links our main table (I often use a table that contains information rather than a lookup or dimension table. The query would work just as well with the tables all switched around, but make less sense when we come back to this query to read it in a few months time, so it is often best to try to write a query that will be nice and easy to understand - lay it out intuitively, use nice indenting so that everything is as clear as it can be. If you go on to teach others, try to instill these characteristics in their queries - especially if you will be troubleshooting them.
It is entirely possible to keep linking more and more tables in this manner.
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
Run Code Online (Sandbox Code Playgroud)
While I forgot to include a table where we might want to join more than one column in the join statement, here is an example. If the models table had brand-specific models and therefore also had a column called brand which linked back to the brands table on the ID field, it could be done as this:
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
and b.brand=d.ID
where
b.ID=1
Run Code Online (Sandbox Code Playgroud)
You can see, the query above not only links the joined tables to the main cars table, but also specifies joins between the already joined tables. If this wasn't done, the result is called a cartesian join - which is dba speak for bad. A cartesian join is one where rows are returned because the information doesn't tell the database how to limit the results, so the query returns all the rows that fit the criteria.
So, to give an example of a cartesian join, lets run the following query:
select
a.ID,
b.model
from
cars a
join models b
+----+--------+
| ID | model |
+----+--------+
| 1 | Sports |
| 1 | Sedan |
| 1 | 4WD |
| 1 | Luxury |
| 2 | Sports |
| 2 | Sedan |
| 2 | 4WD |
| 2 | Luxury |
| 3 | Sports |
| 3 | Sedan |
| 3 | 4WD |
| 3 | Luxury |
| 4 | Sports |
| 4 | Sedan |
| 4 | 4WD |
| 4 | Luxury |
| 5 | Sports |
| 5 | Sedan |
| 5 | 4WD |
| 5 | Luxury |
| 6 | Sports |
| 6 | Sedan |
| 6 | 4WD |
| 6 | Luxury |
| 7 | Sports |
| 7 | Sedan |
| 7 | 4WD |
| 7 | Luxury |
| 8 | Sports |
| 8 | Sedan |
| 8 | 4WD |
| 8 | Luxury |
| 9 | Sports |
| 9 | Sedan |
| 9 | 4WD |
| 9 | Luxury |
| 10 | Sports |
| 10 | Sedan |
| 10 | 4WD |
| 10 | Luxury |
+----+--------+
40 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
Good god, that's ugly. However, as far as the database is concerned, it is exactly what was asked for. In the query, we asked for for the ID from cars and the model from models. However, because we didn't specify how to join the tables, the database has matched every row from the first table with every row from the second table.
Okay, so the boss is back, and he wants more information again. I want the same list, but also include 4WDs in it.
This however, gives us a great excuse to look at two different ways to accomplish this. We could add another condition to the where clause like this:
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
or b.ID=3
Run Code Online (Sandbox Code Playgroud)
While the above will work perfectly well, lets look at it differently, this is a great excuse to show how a union query will work.
We know that the following will return all the Sports cars:
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
Run Code Online (Sandbox Code Playgroud)
And the following would return all the 4WDs:
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=3
Run Code Online (Sandbox Code Playgroud)
So by adding a union all clause between them, the results of the second query will be appended to the results of the first query.
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=1
union all
select
a.ID,
b.model,
c.color
from
cars a
join models b
on a.model=b.ID
join colors c
on a.color=c.ID
join brands d
on a.brand=d.ID
where
b.ID=3
+----+--------+-------+
| ID | model | color |
+----+--------+-------+
| 1 | Sports | Red |
| 8 | Sports | Green |
| 10 | Sports | White |
| 3 | Sports | Black |
| 5 | 4WD | Green |
| 7 | 4WD | White |
| 9 | 4WD | Black |
+----+--------+-------+
7 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
As you can see, the results of the first query are returned first, followed by the results of the second query.
In this example, it would of course have been much easier to simply use the first query, but union queries can be great for specific cases. They are a great way to return specific results from tables from tables that aren't easily joined together - or for that matter completely unrelated tables. There are a few rules to follow however.
Now, you might be wondering what the difference is between using union and union all. A union query will remove duplicates, while a union all will not. This does mean that there is a small performance hit when using union over union all but the results may be worth it - I won't speculate on that sort of thing in this though.
On this note, it might be worth noting some additional notes here.
order by but you can't use the alias anymore. In the query above, appending an order by a.ID would result in an error - as far as the results are concerned, the column is called ID rather than a.ID - even though the same alias has been used in both queries.order by statement, and it must be as the last statement.For the next examples, I am adding a few extra rows to our tables.
I have added Holden to the brands table.
I have also added a row into cars that has the color value of 12 - which has no reference in the colors table.
Okay, the boss is back again, barking requests out -*I want a count of each brand we carry and the number of cars in it!` - Typical, we just get to an interesting section of our discussion and the boss wants more work.
Rightyo, so the first thing we need to do is get a complete listing of possible brands.
select
a.brand
from
brands a
+--------+
| brand |
+--------+
| Ford |
| Toyota |
| Nissan |
| Smart |
| BMW |
| Holden |
+--------+
6 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
Now, when we join this to our cars table we get the following result:
select
a.brand
from
brands a
join cars b
on a.ID=b.brand
group by
a.brand
+--------+
| brand |
+--------+
| BMW |
| Ford |
| Nissan |
| Smart |
| Toyota |
+--------+
5 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
Which is of course a problem - we aren't seeing any mention of the lovely Holden brand I added.
This is because a join looks for matching rows in both tables. As there is no data in cars that is of type Holden it isn't returned. This is where we can use an outer join. This will return all the results from one table whether they are matched in the other table or not:
select
a.brand
from
brands a
left outer join cars b
on a.ID=b.brand
group by
a.brand
+--------+
| brand |
+--------+
| BMW |
| Ford |
| Holden |
| Nissan |
| Smart |
| Toyota |
+--------+
6 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
Now that we have that, we can add a lovely aggregate function to get a count and get the boss off our backs for a moment.
select
a.brand,
count(b.id) as countOfBrand
from
brands a
left outer join cars b
on a.ID=b.brand
group by
a.brand
+--------+--------------+
| brand | countOfBrand |
+--------+--------------+
| BMW | 2 |
| Ford | 2 |
| Holden | 0 |
| Nissan | 1 |
| Smart | 1 |
| Toyota | 5 |
+--------+--------------+
6 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
And with that, away the boss skulks.
Now, to explain this in some more detail, outer joins can be of the left or right type. The Left or Right defines which table is fully included. A left outer join will include all the rows from the table on the left, while (you guessed it) a right outer join brings all the results from the table on the right into the results.
Some databases will allow a full outer join which will bring back results (whether matched or not) from both tables, but this isn't supported in all databases.
Now, I probably figure at this point in time, you are wondering whether or not you can merge join types in a query - and the answer is yes, you absolutely can.
select
b.brand,
c.color,
count(a.id) as countOfBrand
from
cars a
right outer join brands b
on b.ID=a.brand
join colors c
on a.color=c.ID
group by
a.brand,
c.color
+--------+-------+--------------+
| brand | color | countOfBrand |
+--------+-------+--------------+
| Ford | Blue | 1 |
| Ford | White | 1 |
| Toyota | Black | 1 |
| Toyota | Green | 2 |
| Toyota | Red | 1 |
| Nissan | Black | 1 |
| Smart | White | 1 |
| BMW | Blue | 1 |
| BMW | White | 1 |
+--------+-------+--------------+
9 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
So, why is that not the results that were expected? It is because although we have selected the outer join from cars to brands, it wasn't specified in the join to colors - so that particular join will only bring back results that match in both tables.
Here is the query that would work to get the results that we expected:
select
a.brand,
c.color,
count(b.id) as countOfBrand
from
brands a
left outer join cars b
on a.ID=b.brand
left outer join colors c
on b.color=c.ID
group by
a.brand,
c.color
+--------+-------+--------------+
| brand | color | countOfBrand |
+--------+-------+--------------+
| BMW | Blue | 1 |
| BMW | White | 1 |
| Ford | Blue | 1 |
| Ford | White | 1 |
| Holden | NULL | 0 |
| Nissan | Black | 1 |
| Smart | White | 1 |
| Toyota | NULL | 1 |
| Toyota | Black | 1 |
| Toyota | Green | 2 |
| Toyota | Red | 1 |
+--------+-------+--------------+
11 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
As we can see, we have two outer joins in the query and the results are coming through as expected.
Now, how about those other types of joins you ask? What about Intersections?
Well, not all databases support the intersection but pretty much all databases will allow you to create an intersection through a join (or a well structured where statement at the least).
An Intersection is a type of join somewhat similar to a union as described above - but the difference is that it only returns rows of data that are identical (and I do mean identical) between the various individual queries joined by the union. Only rows that are identical in every regard will be returned.
A simple example would be as such:
select
*
from
colors
where
ID>2
intersect
select
*
from
colors
where
id<4
Run Code Online (Sandbox Code Playgroud)
While a normal union query would return all the rows of the table (the first query returning anything over ID>2 and the second anything having ID<4) which would result in a full set, an intersect query would only return the row matching id=3 as it meets both criteria.
Now, if your database doesn't support an intersect query, the above can be easily accomlished with the following query:
select
a.ID,
a.color,
a.paint
from
colors a
join colors b
on a.ID=b.ID
where
a.ID>2
and b.ID<4
+----+-------+----------+
| ID | color | paint |
+----+-------+----------+
| 3 | Blue | Metallic |
+----+-------+----------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
If you wish to perform an intersection across two different tables using a database that doesn't inherently support an intersection query, you will need to create a join on every column of the tables.
Joh*_*Woo 99
好的,我发现这篇文章非常有趣,我想分享一些关于创建查询的知识.谢谢你的Fluffeh.其他可能会读到这个并且可能觉得我错了的人有101%可以自由编辑和批评我的答案.(老实说,我非常感谢纠正我的错误.)
我将在MySQL标签中发布一些常见问题.
鉴于此架构
CREATE TABLE MovieList
(
ID INT,
MovieName VARCHAR(25),
CONSTRAINT ml_pk PRIMARY KEY (ID),
CONSTRAINT ml_uq UNIQUE (MovieName)
);
INSERT INTO MovieList VALUES (1, 'American Pie');
INSERT INTO MovieList VALUES (2, 'The Notebook');
INSERT INTO MovieList VALUES (3, 'Discovery Channel: Africa');
INSERT INTO MovieList VALUES (4, 'Mr. Bean');
INSERT INTO MovieList VALUES (5, 'Expendables 2');
CREATE TABLE CategoryList
(
MovieID INT,
CategoryName VARCHAR(25),
CONSTRAINT cl_uq UNIQUE(MovieID, CategoryName),
CONSTRAINT cl_fk FOREIGN KEY (MovieID) REFERENCES MovieList(ID)
);
INSERT INTO CategoryList VALUES (1, 'Comedy');
INSERT INTO CategoryList VALUES (1, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Romance');
INSERT INTO CategoryList VALUES (2, 'Drama');
INSERT INTO CategoryList VALUES (3, 'Documentary');
INSERT INTO CategoryList VALUES (4, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Comedy');
INSERT INTO CategoryList VALUES (5, 'Action');
Run Code Online (Sandbox Code Playgroud)
题
查找属于至少两个和类别的所有电影. ComedyRomance
解
这个问题有时候非常棘手.似乎这样的查询将是答案: -
SELECT DISTINCT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName = 'Comedy' AND
b.CategoryName = 'Romance'
Run Code Online (Sandbox Code Playgroud)
这绝对是非常错误的,因为它没有产生任何结果.这样的解释是,只有一个有效的价值CategoryName在每行.例如,第一个条件返回true,第二个条件总是false.因此,通过使用AND运算符,两个条件都应该为真; 否则,它将是假的.另一个问题是这样的,
SELECT DISTINCT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName IN ('Comedy','Romance')
Run Code Online (Sandbox Code Playgroud)
并且结果仍然不正确,因为它匹配至少有一个匹配的记录categoryName.在真正的解决方案 是通过计算每部电影的记录实例的数量.实例的数量应与条件中提供的值的总数相匹配.
SELECT a.MovieName
FROM MovieList a
INNER JOIN CategoryList b
ON a.ID = b.MovieID
WHERE b.CategoryName IN ('Comedy','Romance')
GROUP BY a.MovieName
HAVING COUNT(*) = 2
Run Code Online (Sandbox Code Playgroud)
给定架构,
CREATE TABLE Software
(
ID INT,
SoftwareName VARCHAR(25),
Descriptions VARCHAR(150),
CONSTRAINT sw_pk PRIMARY KEY (ID),
CONSTRAINT sw_uq UNIQUE (SoftwareName)
);
INSERT INTO Software VALUES (1,'PaintMe','used for photo editing');
INSERT INTO Software VALUES (2,'World Map','contains map of different places of the world');
INSERT INTO Software VALUES (3,'Dictionary','contains description, synonym, antonym of the words');
CREATE TABLE VersionList
(
SoftwareID INT,
VersionNo INT,
DateReleased DATE,
CONSTRAINT sw_uq UNIQUE (SoftwareID, VersionNo),
CONSTRAINT sw_fk FOREIGN KEY (SOftwareID) REFERENCES Software(ID)
);
INSERT INTO VersionList VALUES (3, 2, '2009-12-01');
INSERT INTO VersionList VALUES (3, 1, '2009-11-01');
INSERT INTO VersionList VALUES (3, 3, '2010-01-01');
INSERT INTO VersionList VALUES (2, 2, '2010-12-01');
INSERT INTO VersionList VALUES (2, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 3, '2011-12-01');
INSERT INTO VersionList VALUES (1, 2, '2010-12-01');
INSERT INTO VersionList VALUES (1, 1, '2009-12-01');
INSERT INTO VersionList VALUES (1, 4, '2012-12-01');
Run Code Online (Sandbox Code Playgroud)
题
查找每个软件的最新版本.显示以下列:SoftwareName,Descriptions,LatestVersion(从VersionNo进行柱),DateReleased
解
一些SQL开发人员错误地使用了MAX()聚合函数.他们倾向于像这样创造,
SELECT a.SoftwareName, a.Descriptions,
MAX(b.VersionNo) AS LatestVersion, b.DateReleased
FROM Software a
INNER JOIN VersionList b
ON a.ID = b.SoftwareID
GROUP BY a.ID
ORDER BY a.ID
Run Code Online (Sandbox Code Playgroud)
(大多数RDBMS在此处生成语法错误,因为没有在group by子句上指定一些非聚合列)结果LatestVersion在每个软件上产生正确但显然DateReleased是不正确的.MySQL不支持Window Functions,Common Table Expression但正如一些RDBMS已经做的那样.解决此问题的方法是创建一个在每个软件上subquery获得最大值versionNo,然后在其他表上连接的最大值.
SELECT a.SoftwareName, a.Descriptions,
b.LatestVersion, c.DateReleased
FROM Software a
INNER JOIN
(
SELECT SoftwareID, MAX(VersionNO) LatestVersion
FROM VersionList
GROUP BY SoftwareID
) b ON a.ID = b.SoftwareID
INNER JOIN VersionList c
ON c.SoftwareID = b.SoftwareID AND
c.VersionNO = b.LatestVersion
GROUP BY a.ID
ORDER BY a.ID
Run Code Online (Sandbox Code Playgroud)
就是这样.我很快就会发布另一个,因为我记得有关标签的任何其他FAQMySQL.感谢您阅读这篇小文章.我希望你至少从中得到一些知识.
更新1
给出架构
CREATE TABLE userList
(
ID INT,
NAME VARCHAR(20),
CONSTRAINT us_pk PRIMARY KEY (ID),
CONSTRAINT us_uq UNIQUE (NAME)
);
INSERT INTO userList VALUES (1, 'Fluffeh');
INSERT INTO userList VALUES (2, 'John Woo');
INSERT INTO userList VALUES (3, 'hims056');
CREATE TABLE CONVERSATION
(
ID INT,
FROM_ID INT,
TO_ID INT,
MESSAGE VARCHAR(250),
DeliveryDate DATE
);
INSERT INTO CONVERSATION VALUES (1, 1, 2, 'hi john', '2012-01-01');
INSERT INTO CONVERSATION VALUES (2, 2, 1, 'hello fluff', '2012-01-02');
INSERT INTO CONVERSATION VALUES (3, 1, 3, 'hey hims', '2012-01-03');
INSERT INTO CONVERSATION VALUES (4, 1, 3, 'please reply', '2012-01-04');
INSERT INTO CONVERSATION VALUES (5, 3, 1, 'how are you?', '2012-01-05');
INSERT INTO CONVERSATION VALUES (6, 3, 2, 'sample message!', '2012-01-05');
Run Code Online (Sandbox Code Playgroud)
题
查找两个用户之间的最新对话.
解
SELECT b.Name SenderName,
c.Name RecipientName,
a.Message,
a.DeliveryDate
FROM Conversation a
INNER JOIN userList b
ON a.From_ID = b.ID
INNER JOIN userList c
ON a.To_ID = c.ID
WHERE (LEAST(a.FROM_ID, a.TO_ID), GREATEST(a.FROM_ID, a.TO_ID), DeliveryDate)
IN
(
SELECT LEAST(FROM_ID, TO_ID) minFROM,
GREATEST(FROM_ID, TO_ID) maxTo,
MAX(DeliveryDate) maxDate
FROM Conversation
GROUP BY minFROM, maxTo
)
Run Code Online (Sandbox Code Playgroud)
Flu*_*feh 63
好的,现在老板再次爆发了 - 我想要一份包含该品牌的所有汽车清单以及我们拥有的这个品牌的总数!
这是一个很好的机会,可以在我们的SQL好东西中使用下一个技巧 - 子查询.如果您不熟悉该术语,则子查询是在另一个查询中运行的查询.有许多不同的方法可以使用它们.
对于我们的请求,让我们首先将一个简单的查询放在一起,列出每辆汽车和品牌:
select
a.ID,
b.brand
from
cars a
join brands b
on a.brand=b.ID
Run Code Online (Sandbox Code Playgroud)
现在,如果我们想简单地按品牌排序的汽车数量,我们当然可以这样写:
select
b.brand,
count(a.ID) as countCars
from
cars a
join brands b
on a.brand=b.ID
group by
b.brand
+--------+-----------+
| brand | countCars |
+--------+-----------+
| BMW | 2 |
| Ford | 2 |
| Nissan | 1 |
| Smart | 1 |
| Toyota | 5 |
+--------+-----------+
Run Code Online (Sandbox Code Playgroud)
那么,我们应该能够简单地将count函数添加到我们的原始查询中吗?
select
a.ID,
b.brand,
count(a.ID) as countCars
from
cars a
join brands b
on a.brand=b.ID
group by
a.ID,
b.brand
+----+--------+-----------+
| ID | brand | countCars |
+----+--------+-----------+
| 1 | Toyota | 1 |
| 2 | Ford | 1 |
| 3 | Nissan | 1 |
| 4 | Smart | 1 |
| 5 | Toyota | 1 |
| 6 | BMW | 1 |
| 7 | Ford | 1 |
| 8 | Toyota | 1 |
| 9 | Toyota | 1 |
| 10 | BMW | 1 |
| 11 | Toyota | 1 |
+----+--------+-----------+
11 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
可悲的是,不,我们做不到.原因是当我们添加汽车ID(列a.ID)时,我们必须将它添加到组中 - 所以现在,当count函数工作时,每个ID只匹配一个ID.
这是我们可以使用子查询的地方 - 实际上我们可以执行两种完全不同类型的子查询,这些子查询将返回我们需要的相同结果.第一种是简单地将子查询放在select子句中.这意味着每次我们获得一行数据时,子查询将运行,获取一列数据,然后将其弹出到我们的数据行中.
select
a.ID,
b.brand,
(
select
count(c.ID)
from
cars c
where
a.brand=c.brand
) as countCars
from
cars a
join brands b
on a.brand=b.ID
+----+--------+-----------+
| ID | brand | countCars |
+----+--------+-----------+
| 2 | Ford | 2 |
| 7 | Ford | 2 |
| 1 | Toyota | 5 |
| 5 | Toyota | 5 |
| 8 | Toyota | 5 |
| 9 | Toyota | 5 |
| 11 | Toyota | 5 |
| 3 | Nissan | 1 |
| 4 | Smart | 1 |
| 6 | BMW | 2 |
| 10 | BMW | 2 |
+----+--------+-----------+
11 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
和Bam!,这会对我们有用.但是,如果您注意到,则必须为我们返回的每一行数据运行此子查询.即使在这个小例子中,我们只有五个不同的汽车品牌,但是子查询运行了十一次,因为我们有11行数据正在返回.因此,在这种情况下,它似乎不是编写代码的最有效方式.
对于不同的方法,让我们运行子查询并假装它是一个表:
select
a.ID,
b.brand,
d.countCars
from
cars a
join brands b
on a.brand=b.ID
join
(
select
c.brand,
count(c.ID) as countCars
from
cars c
group by
c.brand
) d
on a.brand=d.brand
+----+--------+-----------+
| ID | brand | countCars |
+----+--------+-----------+
| 1 | Toyota | 5 |
| 2 | Ford | 2 |
| 3 | Nissan | 1 |
| 4 | Smart | 1 |
| 5 | Toyota | 5 |
| 6 | BMW | 2 |
| 7 | Ford | 2 |
| 8 | Toyota | 5 |
| 9 | Toyota | 5 |
| 10 | BMW | 2 |
| 11 | Toyota | 5 |
+----+--------+-----------+
11 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
好的,所以我们得到了相同的结果(排序略有不同 - 似乎数据库想要返回我们这次选择的第一列排序的结果) - 但是相同的正确数字.
那么,两者之间有什么区别 - 我们何时应该使用每种类型的子查询?首先,让我们确保我们了解第二个查询的工作原理.我们在from查询的子句中选择了两个表,然后编写了一个查询并告诉数据库它实际上是一个表 - 而数据库对此非常满意.有可能是使用这种方法一些好处(以及一些限制).最重要的是这个子查询运行了一次.如果我们的数据库包含大量数据,那么第一种方法可能会有很大的改进.但是,由于我们将其用作表格,因此我们必须引入额外的数据行 - 以便它们实际上可以连接回我们的数据行.如果我们要在上面的查询中使用简单的连接,我们还必须确保有足够的数据行.如果你还记得,连接只会拉回到那个有匹配数据行双方的加入两侧.如果我们不小心,如果此子查询中没有匹配的行,则可能导致无法从我们的cars表返回有效数据.
现在,回顾第一个子查询,也存在一些局限性.因为我们将数据拉回到一行,所以我们只能撤回一行数据.中所使用的子查询select的查询的子句常常只使用一个聚合函数如sum,count,max或其他类似的聚合函数.他们没有必要,但这通常是他们的写作方式.
因此,在我们继续之前,让我们快速了解一下我们可以使用子查询的其他位置.我们可以在where子句中使用它- 现在,这个例子在我们的数据库中有点做作,有更好的方法来获取以下数据,但看到它只是一个例子,让我们来看看:
select
ID,
brand
from
brands
where
brand like '%o%'
+----+--------+
| ID | brand |
+----+--------+
| 1 | Ford |
| 2 | Toyota |
| 6 | Holden |
+----+--------+
3 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
这将返回一个品牌ID和品牌名称列表(第二栏仅添加给我们展示品牌),其中包含o名称中的字母.
现在,我们可以在where子句中使用此查询的结果:
select
a.ID,
b.brand
from
cars a
join brands b
on a.brand=b.ID
where
a.brand in
(
select
ID
from
brands
where
brand like '%o%'
)
+----+--------+
| ID | brand |
+----+--------+
| 2 | Ford |
| 7 | Ford |
| 1 | Toyota |
| 5 | Toyota |
| 8 | Toyota |
| 9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,即使子查询返回三个品牌ID,我们的汽车表也只有两个品牌的条目.
在这种情况下,为了进一步详细说明,子查询正在运行,就好像我们编写了以下代码:
select
a.ID,
b.brand
from
cars a
join brands b
on a.brand=b.ID
where
a.brand in (1,2,6)
+----+--------+
| ID | brand |
+----+--------+
| 1 | Toyota |
| 2 | Ford |
| 5 | Toyota |
| 7 | Ford |
| 8 | Toyota |
| 9 | Toyota |
| 11 | Toyota |
+----+--------+
7 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
同样,您可以看到子查询与手动输入在从数据库返回时如何更改行的顺序.
在我们讨论子查询时,让我们看看我们可以用子查询做些什么:
select子句中的一些子查询,子句中的一些from子句以及子句中的更多子查询where- 只需记住您放入的每个子查询都会使您的查询更复杂并且可能需要更长的时间执行.如果您需要编写一些有效的代码,那么以多种方式编写查询并查看(通过计时或使用解释计划)这是获得结果的最佳查询可能是有益的.第一种方法可能并不总是最好的方式.
Flu*_*feh 58
我想我会添加一些额外的位,用于提出的提示和技巧.
我看到的一个问题是,我如何从两个表中获得不匹配的行,我看到最常被接受的答案如下所示(基于我们的汽车和品牌表 - 其中Holden列为品牌,但没有出现在汽车表中):
select
a.ID,
a.brand
from
brands a
where
a.ID not in(select brand from cars)
Run Code Online (Sandbox Code Playgroud)
而且是它的工作.
+----+--------+
| ID | brand |
+----+--------+
| 6 | Holden |
+----+--------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
但是在某些数据库中效率不高.这是一个Stack Overflow问题的链接,询问它,如果你想进入细节,这里有一篇很好的深度文章.
简短的回答是,如果优化器没有有效地处理它,那么使用如下的查询获得不匹配的行可能要好得多:
select
a.brand
from
brands a
left join cars b
on a.id=b.brand
where
b.brand is null
+--------+
| brand |
+--------+
| Holden |
+--------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
啊,另一个老人但是好东西 - 旧的你不能在FROM子句中指定目标表'品牌'进行更新.
MySQL不允许您update...在同一个表上运行带有子选择的查询.现在,你可能在想,为什么不把它打成where子句呢?但是,如果您只想更新max()日期为一堆其他行的行,该怎么办?你不能在where子句中完全这样做.
update
brands
set
brand='Holden'
where
id=
(select
id
from
brands
where
id=6);
ERROR 1093 (HY000): You can't specify target table 'brands'
for update in FROM clause
Run Code Online (Sandbox Code Playgroud)
所以,我们做不到那个呃?好吧,不完全是.有一个偷偷摸摸的解决方法,令人惊讶的大量用户不知道 - 虽然它确实包括一些你需要注意的hackery.
您可以将子查询粘贴到另一个子查询中,这会在两个查询之间留下足够的空隙,以便它可以工作.但是,请注意,将查询粘贴到事务中可能是最安全的 - 这将阻止在查询运行时对表进行任何其他更改.
update
brands
set
brand='Holden'
where id=
(select
id
from
(select
id
from
brands
where
id=6
)
as updateTable);
Query OK, 0 rows affected (0.02 sec)
Rows matched: 1 Changed: 0 Warnings: 0
Run Code Online (Sandbox Code Playgroud)
pra*_*988 18
您可以在FROM关键字中使用多个查询的概念.让我举个例子:
SELECT DISTINCT e.id,e.name,d.name,lap.lappy LAPTOP_MAKE,c_loc.cnty COUNTY
FROM (
SELECT c.id cnty,l.name
FROM county c, location l
WHERE c.id=l.county_id AND l.end_Date IS NOT NULL
) c_loc, emp e
INNER JOIN dept d ON e.deptno =d.id
LEFT JOIN
(
SELECT l.id lappy, c.name cmpy
FROM laptop l, company c
WHERE l.make = c.name
) lap ON e.cmpy_id=lap.cmpy
Run Code Online (Sandbox Code Playgroud)
您可以根据需要使用任意数量的表.即使在表子查询中,也可以在必要时使用外连接和联合.
这是一种非常简单的方法,可以涉及表格和字段.
小智 7
希望这能让你在阅读这些东西时找到表格:
mysql> show columns from colors;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(3) | NO | PRI | NULL | auto_increment |
| color | varchar(15) | YES | | NULL | |
| paint | varchar(10) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
781885 次 |
| 最近记录: |