Subquery in the WHERE clause exercise - mysql

I have a migration table with attributes (name, departure year) and a stork table with attributes(name, year of birth). I want to write a query giving the names of all storks that migrated before the birth of the youngest stork which is 2002 (using the migration table in the main query and the Stork table in a sub-request of the WHERE clause). N.B: Not allowed to use a clause such as WHERE, GROUP BY, ORDER BY, LIMIT, unless it is necessary/mandatory
Migration table
Name DEPARTURE YEAR
Annamarie 2001
Felix 2002
Annamarie 2003
Felix 2004
Jonas 2001
Stork table
Name YEAROFBIRTH
Annamarie 1998
Felix 1999
Max 2000
Jonas 2001
Christina 2002
Using the DISTINCT operator gives the desired result with 15 names, removing the DISTINCT operator gives me 25 names with duplicates
SELECT DISTINCT migration.NAME
FROM migration
WHERE migration.DEPARTUREYEAR IN(SELECT stork.YEAROFBIRTH
FROM stork
WHERE stork.YEAROFBIRTH <2002)
I expect to obtain the 15 names without using the DISTINCT operator

Just going by the desired result, and your example's use of the literal year; this should be all you need.
SELECT DISTINCT migration.NAME
FROM migration
WHERE migration.DEPARTUREYEAR < 2002
;
However, if you want to not use a literal year, the subquery can be used to determine it like so:
SELECT DISTINCT migration.NAME
FROM migration
WHERE migration.DEPARTUREYEAR < (SELECT MAX(stork.YEAROFBIRTH) FROM stork)
;

Try this-
SELECT NAME -- You can apply DISTINCT if required
FROM Migration
WHERE Name IN (
SELECT Name FROM Stork
WHERE YEAROFBIRTH < 2002
)

Related

Filter second subquery in UNION by results of first subquery

I'm writing a query to pull contact data based on whether someone is a "high donor" or "low donor", and my query works perfectly except for that it will include someone as both a highDonor and a lowDonor if they're in both categories, when we'd really want to just include them as a highDonor.
(For example: Jay Smith donated $1000 in June and $50 in July. Jay would be listed as both a highDonor and lowDonor.)
To fix this problem, I'm trying to exclude contacts from being lowDonors based on if they were already found to be highDonors. My original query unioned these subqueries, but when I try to alias the individual subqueries and reference highDonors in the second subquery, I get an error.
(Note: this is a shortened version of the full query that focuses in on the pieces that cause the error.)
This is the error I get: #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'highDonors.contactId) as lowDonors ) as highAndLowDonors ' at line 23
SELECT DISTINCT
...
FROM
((
SELECT DISTINCT contactid,
"highDonor" AS highorlowdonor
FROM donation
WHERE donationdate > "2015-07-03"
AND donationamount BETWEEN 100 AND 9999.99)
AS highdonors
UNION ALL
(
SELECT DISTINCT contactid,
"lowDonor" AS highorlowdonor
FROM donation
WHERE donationdate > "2015-07-03"
AND donationamount BETWEEN 1 AND 99.99
WHERE contactid NOT IN highdonors.contactid)
AS lowdonors )
AS highandlowdonors
...
Any thoughts on what's causing the error here? Can I alias two subqueries like this and use the first one inside the second?
You cannot use the result from the first query in the second unless you repeat the first query.
But you could rewrite the query to group by the contactid and get the maximum donationamount. Put your logic in a CASE looking at that maximum.
SELECT contactid,
CASE
WHEN max(donationamount) BETWEEN 100 AND 9999.99
THEN 'highDonor'
WHEN max(donationamount) BETWEEN 1 AND 99.99
THEN 'lowDonor'
END highorlowdonor
FROM donation
WHERE donationdate > '2015-07-03'
GROUP BY contactid
HAVING CASE
WHEN max(donationamount) BETWEEN 100 AND 9999.99
THEN 'highDonor'
WHEN max(donationamount) BETWEEN 1 AND 99.99
THEN 'lowDonor'
END IS NOT NULL;
(Note: Without the HAVING this would include donors who have donated an amount < 1 or an 99.99 < amount < 100 or an amount > 9999.99 but with highorlowdonor being null. Instead of using HAVING I'd rather recommend to adapt your logic to use < (or >) rather than <= (or >=), which BETWEEN is an alias for.)
And BTW: You're using double quotes (") where you should use single quotes (') (to enclose string and date literals).
You cannot alias two subqueries like this and use the first one inside the second. You can try the following:
SELECT DISTINCT contactid,
"highDonor" AS highorlowdonor
FROM donation
WHERE donationdate > "2015-07-03"
AND donationamount BETWEEN 100 AND 9999.99)
AS highdonors
UNION ALL
SELECT DISTINCT contactid,
"lowDonor" AS highorlowdonor
FROM donation
WHERE donationdate > "2015-07-03"
AND donationamount BETWEEN 1 AND 99.99
AND contactid NOT IN (select contact_id
FROM donation
WHERE donationdate > "2015-07-03"
AND donationamount BETWEEN 100 AND 9999.99)
A more efficient way would be store the first union select in a temp table and use that in the union and then use the same temp table in the second union select query to exclude the high donors.

Filtering out some rows

I'd like to filter out some rows in SQL. This is my table.
ID | year
1 2002
1 2003
2 2003
2 2004
3 2002
I'd like to filter the ID's that has got a 2003 in the year column.
That means in this case, there will be no ID's that has got 2003 in the year column.
What is the SQL code I should use?
Your question is a bit ambiguous. One interpretation is to get the list of ids that do not contain the year 2003:
select id
from t
group by id
having sum(year = 2003) = 0
Have a look here http://www.sqlfiddle.com/#!2/bd0d6/3
select id
from dateyear
where year<> 2003
group by id
If you want to list all IDs that do not have a 2003 in the year column then year != 2003, as it was suggested by other users, won't be enough because that will return id = 2 as it contains 2004 too.
A good approach is to list get all IDs and remove from them the ones that contain 2003 in the year. You can do that this way:
SELECT DISTINCT id FROM t
WHERE id NOT IN (
SELECT id FROM t
WHERE YEAR = 2003
)
This will only output id = 3.
Fiddle here.

Group and Sum with dynamic column names

I need to GROUP a table by Year and SUM all the possible Types (unknown) with dynamic column names.
Sample Table:
Type|Year
a 2001
a 2001
c 2002
b 2002
c 2003
a 2003
z 2003
Sample Result:
Year: 2001, Type_a: 2
Year: 2002, Type_c: 1, Type_b: 1
Year: 2003, Type_c: 1, Type_a: 1, Type_z: 1
You could group and sum types using a query like this -
SELECT year, type, COUNT(type) FROM table_name GROUP BY year, type;
It gives another result set, but with data you want.
SELECT year,COUNT(type) from tableName GROUP BY(type)
try that one
SOL is not designed for that, the result could never have a various number of column for each line.
I think the best way to get that is to change the design of your resultset with concatenation of information by example. Or having fixed number of.column filled by null or empty values.
In the other hand you can do it programmaticaly if your language allows dynamic number of column for each row.

mysql first record retrieval

While very easy to do in Perl or PHP, I cannot figure how to use mysql only to extract the first unique occurence of a record.
For example, given the following table:
Name Date Time Sale
John 2010-09-12 10:22:22 500
Bill 2010-08-12 09:22:37 2000
John 2010-09-13 10:22:22 500
Sue 2010-09-01 09:07:21 1000
Bill 2010-07-25 11:23:23 2000
Sue 2010-06-24 13:23:45 1000
I would like to extract the first record for each individual in asc time order.
After sorting the table is ascending time order, I need to extract the first unique record by name.
So the output would be :
Name Date Time Sale
John 2010-09-12 10:22:22 500
Bill 2010-07-25 11:23:23 2000
Sue 2010-06-24 13:23:45 1000
Is this doable in an easy fashion with mySQL?
I think that something along the lines of
select name, date, time, sale from mytable order by date, time group by name;
will get you what you're looking for
you need to perform a groupwise max or groupwise min
see below or http://pastie.org/973117 for an example
select
u.user_id,
u.username,
latest.comment_id
from
users u
left outer join
(
select
max(comment_id) as comment_id,
user_id
from
user_comment
group by
user_id
) latest on u.user_id = latest.user_id;
In databases, there really is no "first" or "last" record; think of each record as its own, non-positional entity in the table. The only positions they have are when you give them one, say, using ORDER BY.
This will give you what you want. It might not be efficient, but it works.
select Name, Date, Time, Sale from
(select Name, Date, Time, Sale from MyTable
order by Date asc, Time asc) MyTable_subquery_name
group by Name
Note: MyTable_subquery_name is just a dummy name for the subquery. MySQL will give the error ERROR 1248 (42000): Every derived table must have its own alias without it.
If only GROUP BY and ORDER BY were communicative operations, then this wouldn't have to be a subquery.

Running count of distinct values in Access

I have data stored as below in an MS Access database:
Date User
20090101 1001
20090101 1002
20090102 1001
20090103 1001
20090103 1003
I'm attempting to create a query which shows the daily running count of unique users. For example:
Date Daily Count Unique User Running Count
20090101 2 2
20090102 1 2
20090103 2 3
What's the best way to achieve this?
In most SQL implementations you could select using the aggregate function count(distinct user). But Access doesn't support that construct. I think the best you could do is to select the distinct values in a subquery and count them.
I was going to write a query but this link seems to do a good job.
HTH
Tom
Your query will look something like this...can't test it without the data though:
SELECT Date, Count(Date) As [Daily Count], Count(User) As [Unique User Running Count]
FROM TableName
GROUP BY Date
I did it! A simple solution is the best: no SQL coding needed.
In Access, Query Design,
Column 1 =
Field=Date
tablename=yourname
Total=Groupby
Column2 =
Field=Date
Table=yourname
Total=Count