Access SQL query with JOIN and a COUNT statement not counting anything - sql

I have two tables in MS Access. One for planets and one for the type of star it orbits.
I want to get a count of the planets per star type...so something like:
+----------+--------------------+
| StarType | PlanetsPerStarType |
+----------+--------------------+
| A | 4 |
| B | 1 |
| C | 7 |
+----------+--------------------+
So I wrote this SQL query:
SELECT StarType, COUNT(PlanetName) AS PlanetsPerStarType
FROM Planets AS p
LEFT JOIN StarClass AS s ON p.sid = s.sid
GROUP BY starType, PlanetName
But it just lists all the planets and a 1 for all the star types, it doesn't really count at all.
What could I be doing wrong?

By grouping by the starType and the PlanetName, the count is returning the number of records in each starType & PlanetName combination, which, unless you have more than one planet of the same name orbiting your star, will always be one.
For example, given the data:
+-----------+------------------+
| StarType | PlanetName |
+-----------+------------------+
| G2V | Mars |
| G2V | Earth |
| G2V | Venus |
| Red Dwarf | Omicron Persei 8 |
| Red Dwarf | Vergon 6 |
+-----------+------------------+
Grouping by StarType and PlanetName will yield exactly the same data, since there are no repeating StarType and PlanetName combinations which would be consolidated into a group.
As such, the SQL code:
select t.StarType, count(t.PlanetName) as Planets
from YourTable t
group by t.StarType, t.PlanetName
Would yield:
+-----------+---------+
| StarType | Planets |
+-----------+---------+
| G2V | 1 |
| G2V | 1 |
| G2V | 1 |
| Red Dwarf | 1 |
| Red Dwarf | 1 |
+-----------+---------+
Since each group contains exactly one record.
If, instead, we group only by StarType, the Count aggregate function will return the number of records associated with each StarType:
select t.StarType, count(t.PlanetName) as Planets
from YourTable t
group by t.StarType
+-----------+---------+
| StarType | Planets |
+-----------+---------+
| G2V | 3 |
| Red Dwarf | 2 |
+-----------+---------+

Related

Perform join on joined queries with N number of rows per type

This is a follow-up question to [stackexchange]: Perform join on joined queries. Please read this first.
The initial question on how to transpose rows based on a certain type has been answered, but now I also need this extend to a potential N number of rows.
Example table for this case:
+------+--------------+--------------+--------+----------+
| type | information1 | information2 | Notes | uniqueID |
+------+--------------+--------------+--------+----------+
| IN | infoA | anotherinfo1 | NotesA | SN1 |
+------+--------------+--------------+--------+----------+
| IN | infoB | anotherinfo2 | NotesB | SN1 |
+------+--------------+--------------+--------+----------+
| OUT | infoC | anotherinto3 | NotesC | SN2 |
+------+--------------+--------------+--------+----------+
| OUT | infoD | anotherinto4 | NotesD | SN2 |
+------+--------------+--------------+--------+----------+
Basically I think I would need a loop over each 'IN' and 'OUT' type. In order to transpose the N IN and OUT to columns and then later join with another table. No idea how to do this.
Table (B) in related SO ticket shows the target output and would need to extend by N columns for Info N etc.

Query M:N contains

I am trying to filter a set of tables that includes an M:N junction table in Android Room (SQLite).
An image can have many subjects. I'd like to allow filtering by a subject, so that I get a row with complete image information (including all subjects). So if an image had (National Park, Yosemite) filtering for either would result in one row with both keywords. Unless I messed something up, a typical join will result in multiple rows such that matching Yosemite would get the right image, but you'd be lacking National Park. I came up with this:
SELECT *,
(SELECT GROUP_CONCAT(name)
FROM meta_subject_junction
JOIN subject
ON subject.id = meta_subject_junction.subjectId
WHERE meta_subject_junction.metaId = meta.id) AS keywords,
(SELECT documentUri
FROM image_parent
WHERE meta.parentId = image_parent.id ) AS parentUri
FROM meta
Now this gets me the complete rows, but I think at this point I'd need to:
WHERE keywords LIKE(%YOSEMITE%)
and I think the LIKE is less than ideal, not to mention an imprecise match. Is there a better way to accomplish this? Thanks, this is bending my novice SQL brain.
Further details
meta
+----+----------+--+
| id | name | |
+----+----------+--+
| 1 | yosemite | |
| 2 | bryce | |
| 3 | flowers | |
+----+----------+--+
subject
+----+---------------+--+
| id | name | |
+----+---------------+--+
| 1 | National Park | |
| 2 | Yosemite | |
| 3 | Tulip | |
+----+---------------+--+
junction
+--------+-----------+
| metaId | subjectId |
+--------+-----------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 3 |
+--------+-----------+
Although I may have done something wrong, as far as I can tell Android Room doesn't like:
+----+-----------+---------------+
| id | name | subject |
+----+-----------+---------------+
| 1 | yosemite | National Park |
| 1 | yosemite | Yosemite |
+----+-----------+---------------+
so I'm trying to reduce the rows:
+----+-----------+-------------------------+
| id | name | subject |
+----+-----------+-------------------------+
| 1 | yosemite | National Park, Yosemite |
+----+-----------+-------------------------+
which the above query does. However, I also want to query for a subject. So that National Park filter will yield:
+----+-----------+-------------------------+
| id | name | subject |
+----+-----------+-------------------------+
| 1 | yosemite | National Park, Yosemite |
| 2 | bryce | National Park |
+----+-----------+-------------------------+
I'd like to be more precise/efficient than LIKE with the already 'concat' subject. Most of my attempts end up with no results in Room (multi-row) or reducing the subject to only the filter keyword.
Update
Here's a test I've been using to compare the actual SQL results from a query to what Android Room ends up with:
http://sqlfiddle.com/#!7/0ac11/10/0
That join query is interpreted as four objects in Android Room, so I'm trying to reduce the rows, but retain the full subject results while filtering for any image containing the subject keyword.
If you want multiple keywords, then where and group by and having can be used:
select image_id
from image_subject
where subject_id in ('a', 'b', 'c') -- whatever
group by image-id
having count(distinct subject_id) = 3; -- same count as in `where`
This gets the result I need, though I'd love to hear a better option if this is particularly inefficient.
SELECT meta.*,
(SELECT GROUP_CONCAT(name)
FROM junction
JOIN subject
ON subject.id = junction.subjectId
WHERE junction.metaId = meta.id) AS keywords,
junction.subjectId
FROM meta
LEFT JOIN junction ON junction.metaId = meta.id
WHERE subjectId IN (1,2)
GROUP BY meta.id
+----+----------+------------------------+-----------+
| id | name | keywords | subjectId |
+----+----------+------------------------+-----------+
| 1 | yosemite | National Park,Yosemite | 2 |
| 2 | bryce | National Park | 1 |
+----+----------+------------------------+-----------+
http://sqlfiddle.com/#!7/86a76/13

When Querying Many-To-Many Relationship in SQL, Return Multiple Connections As an Array In Single Row?

Basically, I have 3 tables, titles, providers, and provider_titles.
Let's say they look like this:
| title_id | title_name |
|------------|----------------|
| 1 | San Andres |
| 2 |Human Centipede |
| 3 | Zoolander 2 |
| 4 | Hot Pursuit |
| provider_id| provider_name |
|------------|----------------|
| 1 | Hulu |
| 2 | Netflix |
| 3 | Amazon_Prime |
| 4 | HBO_GO |
| provider_id| title_id |
|------------|----------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 3 | 1 |
| 3 | 3 |
| 4 | 4 |
So, clearly there are titles with multiple providers, yeah? Typical many-to-many so far.
So what I'm doing to query it is with a JOIN like the following:
SELECT * FROM provider_title JOIN provider ON provider_title.provider_id = provider.provider_id JOIN title ON title.title_id = provider_title.title_id WHERE provider.name IN ('Netflix', 'HBO_GO', 'Hulu', 'Amazon_Prime')
Ok, now to the actual issue. I don't want repeated title names back, but I do want all of the providers associated with the title. Let me explain with another table. Here is what I am getting back with the current query, as is:
| provider_id| provider_name | title_id | title_name |
|------------|---------------|----------|---------------|
| 1 | Hulu | 1|San Andreas |
| 1 | Hulu | 2|Human Centipede|
| 2 | Netflix | 1|San Andreas |
| 3 | Amazon_Prime | 1|San Andreas |
| 3 | Amazon_prime | 3|Zoolander 2 |
| 4 | HBO_GO | 4|Hot Pursuit |
But what I really want would be something more like
| provider_id| provider_name |title_id| title_name|
|------------|-----------------------------|--------|-----------|
| [1, 2, 3] |[Hulu, Netflix, Amazon_Prime]| 1|San Andreas|
Meaning I only want distinct titles back, but I still want each title's associated providers. Is this only possible to do post-sql query with logic iterating through the returned rows?
Depending on your database engine, there may be an aggregation function to help achieve this.
For example, this SQLfiddle demonstrates the postgres array_agg function:
SELECT t.title_id,
t.title_name,
array_agg( p.provider_id ),
array_agg( p.provider_name )
FROM provider_title as pt
JOIN
provider as p
ON pt.provider_id = p.provider_id
JOIN title as t
ON t.title_id = pt.title_id
GROUP BY t.title_id,
t.title_name
Other database engines have equivalents. For example:
mySQL has group_concat
Oracle has listagg
sqlite has group_concat (as well!)
If your database isn't covered by the above, you can google '[Your database engine] aggregate comma delimited string'

Count numbers in single row - SQL

is it possible to return count of values in single row?
For example this is test table and I want to count of daily_typing_pages
SQL> SELECT * FROM employee_tbl;
+------+------+------------+--------------------+
| id | name | work_date | daily_typing_pages |
+------+------+------------+--------------------+
| 1 | John | 2007-01-24 | 250 |
| 2 | Ram | 2007-05-27 | 220 |
| 3 | Jack | 2007-05-06 | 170 |
| 3 | Jack | 2007-04-06 | 100 |
| 4 | Jill | 2007-04-06 | 220 |
| 5 | Zara | 2007-06-06 | 300 |
| 5 | Zara | 2007-02-06 | 350 |
+------+------+------------+--------------------+
Result of this count should be : 1610 how ever if I simply count() AROUND it return:
SQL>SELECT COUNT(daily_typing_pages) FROM employee_tbl ;
+---------------------------+
| COUNT(daily_typing_pages) |
+---------------------------+
| 7 |
+---------------------------+
1 row in set (0.01 sec)
So it return number of rows instead of count single row.
Is there some way how to do things like I want without using external programming language which will count it for me?
Thanks
You want SUM instead of COUNT. COUNT merely counts the number of records, you want them summed.
You didn't mention your DBMS, but see for example, for sql server this
Did you mean you want to summarize alle numbers of daily_typing_pages ?
So you can use sum(daily_typing_pages):
SELECT SUM(daily_typing_pages) FROM employee_tbl

SQL query: where array is in array

I've got sample data in database:
id (int) name (varchar) parts (varchar)
1 some_element wheel, bearing, hinge, servo
2 another_element bearing, servo, lift
3 third_element motor, wire
I want to filter results by parts. For example:
I'm typing wheel, servo - no results
I'm typing wheel, bearing, servo, hinge - returns some_element record
I'm typing bearing, servo, lift, wheel, bearing, hinge - it returns some_element and another_element
How to construct SQL query? Is there any other data type better for parts field?
Do some normalization so that you can write queries more easily and won't have such anomalies.
You'll need another structure, like:
The element table
+----+---------------+
| id | name |
+----+---------------+
| 1 | some_element |
+----+---------------+
| 2 | another_elem |
+----+---------------+
| 3 | third_elem |
+----+---------------+
The part table
+----+----------+
| id | name |
+----+----------+
| 1 | wheel |
+----+----------+
| 2 | bearing |
+----+----------+
| 3 | hinge |
+----+----------+
| 4 | servo |
+----+----------+
etc..
And another, such as element_parts to connect the other two by an m:n relation
+----+---------+---------+
| id | elem_id | part_id |
+----+----------+--------+
| 1 | 1 | 1 |
+----+---------+---------+
| 2 | 1 | 2 |
+----+---------+---------+
| 3 | 1 | 3 |
+----+---------+---------+
| 4 | 2 | 3 |
+----+---------+---------+
| 5 | 2 | 4 |
+----+---------+---------+
etc..
And now you can write a query to, say, filter elements that contain (or need) wheel and servo (adapting this question's accepted answer):
select *
from element e
where 2 = (
select count(distinct p.id)
from element_parts ep
inner join part p on p.id = ep.part_id
where p.name in ('wheel', 'servo')
and ep.elem_id = e.id
);