How can I make this query better?

750 views Asked by At

I'm learning SQL through GALAXQL http://sol.gfxile.net/galaxql.html

Im on lesson 17 - GROUP BY/HAVING

Here is the scenario:

Let's look at couple of SELECT operations we haven't covered yet, namely GROUP BY and HAVING.

The syntax for these operations looks like this:

SELECT columns FROM table GROUP BY column HAVING expression

The GROUP BY command in a SELECT causes several output rows to be combined into a single row. This can be very useful if, for example, we wish to generate new statistical data as a table.

For example, to find out the highest intensities from stars for each class, we would do:

Select Class, Max(Intensity) As Brightness From Stars Group By Class Order By Brightness Desc

The HAVING operator works pretty much the same way as WHERE, except that it is applied after the grouping has been done. Thus, we could calculate the sum of brightnesses per class, and crop out the classes where the sum is higher than, say, 150.

SELECT class, SUM(intensity) AS brightness FROM stars GROUP BY class HAVING brightness < 150 ORDER BY brightness DESC

We could refer to columns that are not selected in the HAVING clause, but the results might be difficult to understand. You should be able to use the aggregate functions in the HAVING clause (for example, brightness < MAX(brightness)*0.5, but this seems to crash the current version of SQLite.

When combined with joins, GROUP BY becomes rather handy. To find out the number of planets per star, we can do:

SELECT stars.starid AS starid, COUNT(planets.planetid) AS planet_count FROM planets, stars WHERE stars.starid=planets.starid GROUP BY stars.starid

Hilight the star with most orbitals (combined planets and moons). (Note that the validation query is somewhat heavy, so be patient after pressing "Ok, I'm done..").


Here was my answer

SELECT stars.starid AS HighStar, 
(COUNT(planets.planetid) + COUNT(moons.moonid)) AS OrbitalsTotal 
FROM stars
LEFT OUTER JOIN planets
ON stars.starid = planets.starid
LEFT OUTER JOIN moons
ON planets.planetid = moons.planetid
GROUP BY stars.starid
ORDER BY OrbitalsTotal DESC;

This query showed me that the star with the most oribtals has 170 orbitals

So then:

INSERT INTO hilight SELECT result.HighStar
FROM result
INNER JOIN stars
ON result.HighStar = stars.starid
WHERE result.OrbitalsTotal = 170

My question to you is how can I make this query better? I don't want to have to hard code the 170 orbitals and I dont want to have to create a second query to insert the data.

5

There are 5 answers

1
Dale M On BEST ANSWER
SELECT stars.starid AS HighStar, 
       (COUNT(planets.planetid) + COUNT(moons.moonid)) AS OrbitalsTotal 
FROM stars
     LEFT OUTER JOIN
     planets ON stars.starid = planets.starid
     LEFT OUTER JOIN
     moons ON planets.planetid = moons.planetid
GROUP BY stars.starid
HAVING OrbitalsTotal = (SELECT MAX(Orbitals)
                        FROM (SELECT (COUNT(planets.planetid) + COUNT(moons.moonid)) Orbitals
                              FROM stars
                                   LEFT OUTER JOIN
                                   planets ON stars.starid = planets.starid
                                   LEFT OUTER JOIN
                                   moons ON planets.planetid = moons.planetid
                              GROUP BY stars.starid))
0
CL. On

Just use your first query, and add the clause LIMIT 1 to return only the first record:

INSERT INTO hilight
SELECT stars.starid AS HighStar
FROM stars
LEFT OUTER JOIN planets
ON stars.starid = planets.starid
LEFT OUTER JOIN moons
ON planets.planetid = moons.planetid
GROUP BY stars.starid
ORDER BY COUNT(planets.planetid) + COUNT(moons.moonid) DESC
LIMIT 1
2
Dale M On

If you start your first query with SELECT TOP(1) ... then this will give you the first result only. The TOP() syntax can be used with a hard number which gives that many rows or a percentage which gives that percentage of the total.

1
roymeo On

By Chapter 17 of GalaXQL, we haven't learned LIMIT, and I think we can do it without all the LEFT OUTER JOINS (borrowing from Dale M's (which was missing "AS" before "Orbitals" though it still worked?!)) to be a bit closer to what we saw in the last example of that section.

So how about:

DELETE FROM hilight;
INSERT INTO hilight 
SELECT HighStar FROM (
    SELECT stars.starid AS HighStar, 
        (COUNT(planets.planetid) + COUNT(moons.moonid)) AS OrbitalsTotal 
    FROM stars, planets, moons 
    WHERE stars.starid = planets.starid
        AND planets.planetid = moons.planetid
    GROUP BY stars.starid
    HAVING OrbitalsTotal = (
        SELECT MAX(Orbitals) FROM (
            SELECT (COUNT(planets.planetid) + COUNT(moons.moonid)) AS Orbitals
            FROM stars, planets, moons
            WHERE stars.starid = planets.starid
                AND planets.planetid = moons.planetid
            GROUP BY stars.starid))
);
SELECT * FROM hilight;

With LIMIT, we can also avoid the LEFT OUTER JOINs and not have to duplicate our selection:

DELETE FROM hilight;
INSERT INTO hilight
SELECT bodygroup FROM (
    SELECT stars.starid as bodygroup, 
        (COUNT(planets.planetid)+COUNT(moons.moonid)) AS bodycount
    FROM stars, planets, moons 
    WHERE stars.starid=planets.starid 
        AND planets.planetid=moons.planetid 
    GROUP BY stars.starid
    ORDER BY bodycount DESC) 
LIMIT 1;
SELECT * FROM hilight;

Or using a CREATE VIEW to try to keep from doubling some effort while not using LIMIT, we could:

DELETE FROM hilight;    
CREATE VIEW bodyview AS
    SELECT stars.starid as bodygroup,
        (COUNT(planets.planetid)+COUNT(moons.moonid)) AS bodycount
    FROM stars, planets, moons
    WHERE stars.starid=planets.starid
       AND planets.planetid=moons.planetid
    GROUP BY stars.starid
    ORDER BY bodycount DESC;
INSERT INTO hilight SELECT bodygroup FROM bodyview  
    WHERE bodycount = (SELECT MAX(bodycount) FROM bodyview );
DROP VIEW bodyview;
SELECT * FROM hilight;
1
solarflare97 On

Star 22336 does not have 170 orbitals.

It has 97 orbitals.

Check the number of moons.

SELECT COUNT(moons.moonid) as moon_count
FROM stars JOIN planets
ON stars.starid=planets.starid
JOIN moons
ON planets.planetid=moons.planetid
WHERE stars.starid=22336

This gives 79 moons.

Now check the number of planets.

SELECT COUNT(planets.planetid) AS planet_count
FROM stars JOIN planets
ON stars.starid=planets.starid
WHERE stars.starid=22336

This gives 18 planets.

The SQL below computes the orbital counts correctly and accomplishes the task.

INSERT INTO hilight
SELECT pc.starid FROM

(SELECT p.starid, COUNT(p.planetid) AS p_count
FROM planets AS p
GROUP BY p.starid) AS pc

JOIN

(SELECT p.starid, COUNT(m.moonid) AS m_count
FROM planets AS p JOIN moons AS m
ON p.planetid=m.planetid
GROUP BY p.starid) AS mc

ON pc.starid=mc.starid

ORDER BY p_count+m_count DESC
LIMIT 1