SQL query return data from multiple tables

前端 未结 6 958
被撕碎了的回忆
被撕碎了的回忆 2020-11-21 04:23

I would like to know the following:

  • how to get data from multiple tables in my database?
  • what types of methods are there to do this?
  • what are
6条回答
  •  感动是毒
    2020-11-21 05:14

    Part 2 - Subqueries

    Okay, now the boss has burst in again - I want a list of all of our cars with the brand and a total of how many of that brand we have!

    This is a great opportunity to use the next trick in our bag of SQL goodies - the subquery. If you are unfamiliar with the term, a subquery is a query that runs inside another query. There are many different ways to use them.

    For our request, lets first put a simple query together that will list each car and the brand:

    select
        a.ID,
        b.brand
    from
        cars a
            join brands b
                on a.brand=b.ID
    

    Now, if we wanted to simply get a count of cars sorted by brand, we could of course write this:

    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 |
    +--------+-----------+
    

    So, we should be able to simply add in the count function to our original query right?

    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)
    

    Sadly, no, we can't do that. The reason is that when we add in the car ID (column a.ID) we have to add it into the group by - so now, when the count function works, there is only one ID matched per ID.

    This is where we can however use a subquery - in fact we can do two completely different types of subquery that will return the same results that we need for this. The first is to simply put the subquery in the select clause. This means each time we get a row of data, the subquery will run off, get a column of data and then pop it into our row of data.

    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)
    

    And Bam!, this would do us. If you noticed though, this sub query will have to run for each and every single row of data we return. Even in this little example, we only have five different Brands of car, but the subquery ran eleven times as we have eleven rows of data that we are returning. So, in this case, it doesn't seem like the most efficient way to write code.

    For a different approach, lets run a subquery and pretend it is a table:

    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)
    

    Okay, so we have the same results (ordered slightly different - it seems the database wanted to return results ordered by the first column we picked this time) - but the same right numbers.

    So, what's the difference between the two - and when should we use each type of subquery? First, lets make sure we understand how that second query works. We selected two tables in the from clause of our query, and then wrote a query and told the database that it was in fact a table instead - which the database is perfectly happy with. There can be some benefits to using this method (as well as some limitations). Foremost is that this subquery ran once. If our database contained a large volume of data, there could well be a massive improvement over the first method. However, as we are using this as a table, we have to bring in extra rows of data - so that they can actually be joined back to our rows of data. We also have to be sure that there are enough rows of data if we are going to use a simple join like in the query above. If you recall, the join will only pull back rows that have matching data on both sides of the join. If we aren't careful, this could result in valid data not being returned from our cars table if there wasn't a matching row in this subquery.

    Now, looking back at the first subquery, there are some limitations as well. because we are pulling data back into a single row, we can ONLY pull back one row of data. Subqueries used in the select clause of a query very often use only an aggregate function such as sum, count, max or another similar aggregate function. They don't have to, but that is often how they are written.

    So, before we move on, lets have a quick look at where else we can use a subquery. We can use it in the where clause - now, this example is a little contrived as in our database, there are better ways of getting the following data, but seeing as it is only for an example, lets have a look:

    select
        ID,
        brand
    from
        brands
    where
        brand like '%o%'
    
    +----+--------+
    | ID | brand  |
    +----+--------+
    |  1 | Ford   |
    |  2 | Toyota |
    |  6 | Holden |
    +----+--------+
    3 rows in set (0.00 sec)
    

    This returns us a list of brand IDs and Brand names (the second column is only added to show us the brands) that contain the letter o in the name.

    Now, we could use the results of this query in a where clause this:

    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)
    

    As you can see, even though the subquery was returning the three brand IDs, our cars table only had entries for two of them.

    In this case, for further detail, the subquery is working as if we wrote the following code:

    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)
    

    Again, you can see how a subquery vs manual inputs has changed the order of the rows when returning from the database.

    While we are discussing subqueries, lets see what else we can do with a subquery:

    • You can place a subquery within another subquery, and so on and so on. There is a limit which depends on your database, but short of recursive functions of some insane and maniacal programmer, most folks will never hit that limit.
    • You can place a number of subqueries into a single query, a few in the select clause, some in the from clause and a couple more in the where clause - just remember that each one you put in is making your query more complex and likely to take longer to execute.

    If you need to write some efficient code, it can be beneficial to write the query a number of ways and see (either by timing it or by using an explain plan) which is the optimal query to get your results. The first way that works may not always be the best way of doing it.

提交回复
热议问题