SQL query. An unusual join. DB implemented in sqlite-3 - sql

This is essentially a question about constructing an SQL query. The db is implemented with sqlite3. I am a relatively new user of SQL.
I have two tables and want to join them in an unusual way. The following is an example to explain the problem.
Table 1 (t1):
id year name
-------------------------
297 2010 Charles
298 2011 David
300 2010 Peter
301 2011 Richard
Table 2 (t2)
id year food
---------------------------
296 2009 Bananas
296 2011 Bananas
297 2009 Melon
297 2010 Coffee
297 2012 Cheese
298 2007 Sugar
298 2008 Cereal
298 2012 Chocolate
299 2000 Peas
300 2007 Barley
300 2011 Beans
300 2012 Chickpeas
301 2010 Watermelon
I want to join the tables on id and year. The catch is that (1) id must match exactly, but if there is no exact match in Table 2 for the year in Table 1, then I want to choose the year that is the next (lower) available. A selection of the kind that I want to produce would give the following result
id year matchyr name food
-------------------------------------------------
297 2010 2010 Charles Coffee
298 2011 2008 David Cereal
300 2010 2007 Peter Barley
301 2011 2010 Richard Watermelon
To summarise, id=297 had an exact match for year=2010 given in Table 1, so the corresponding line for id=297, year=2010 is chosen from Table 2. id=298, year=2011 did not have a matching year in Table 2, so the next available year (less than 2011) is chosen. As you can see, I would also like to know what that matched year (whether exactly , or inexactly) actually was.
I would very much appreciate (1) an indication (yes/no answer) of whether this is possible to do in SQL alone, or whether I need to look outside SQL, and (2) a solution, if that is not too onerous.

Sure, you can do this in SQL. What you need for the first step is a query which joins t1 and t2 on id, and gives you the various possible years (EDIT: fixed this sentence, the original one was incorporating the next step into it as well). You can create a view called t3 which gives you this as follows:
CREATE VIEW [t3] AS
SELECT t1.id as id, t2.year as year FROM t1 INNER JOIN t2 ON (t1.id = t2.id AND T2.year <= t1.year)
From here, you'll want to get the maximum year from t3 per id:
CREATE VIEW [t4] AS
SELECT id, MAX(year) FROM t3 GROUP BY id
You should be able to join t4 back onto t1 and t2 to get what you want. Of course, there are ways to shorten this, but it should get you thinking along the right lines. Also, I'm guessing it's just an example, but please don't call your tables t1, t2, etc.

Related

Joins Specific Querying

I'm using SQL Server 2017 and I have a question on how to return specific values (I'm not sure 100% if I should be using joins or subqueries.) I tried to put an example below of what I'm trying to do and hopefully someone can help me out with this.
I have two tables below:
Table 1
Record
Topic
100
History
101
Science
102
Art
103
Music
Table 2
Record
Course
100
Intro
100
Intermediate
101
Intermediate
101
Advanced
102
Intro
102
Intermediate
103
Intermediate
103
Advanced
I want to join the two tables together, but only bring back the columns 'Record' and 'Topic' ONLY where the Record DOES NOT have an 'Intro' course attached to it.
So I would want to bring back the following results (since both Courses do not have an Intro class):
Record
Topic
101
Science
103
Music
So far I've had no luck and am bringing back more records than I should so any help or guidance would be appreciated. Thank you in advance.
You could use exists logic here:
SELECT Record, Topic
FROM Table1 t1
WHERE NOT EXISTS (
SELECT 1
FROM Table2 t2
WHRE t2.Record = t1.Record AND
t2.Course = 'Intro'
);

SQL with Bob and John owing each other money

I have got the following 3 fields in a file: person_ows person_is_owed amount
Example content:
Bob John 100
John Bob 110
What does a SQL look like that produces:
Bob John 100 110
John Bob 110 100
Sorry if this is a trivial question, but I am just trying to learn SQL and I find it really like HELL!
So, what you need is to be able to JOIN two rows. In this case you'll probably want an OUTER JOIN assuming that there isn't always a match of each owing the other. Now you just need to come up with your JOIN criteria, which in this case is going to be based on the names (person_owes and person_is_owed):
SELECT
T1.person_owes,
T1.person_is_owed,
T1.amount AS owes_amount,
COALESCE(T2.amount, 0) AS is_owed_amount
FROM
My_Table T1
LEFT OUTER JOIN My_Table T2 ON T2.person_is_owed = T1.person_owes
The COALESCE is just to make sure that when there is no match that you get a value of 0 instead of NULL.
Also, this assumes that there is only going to be one of each combination of person_owes and person_is_owed. If you might have two rows showing that John owes Bill two different amounts of money then you would have to adjust the SQL above and it would be a bit more complex.
If you plan to use SQL much then you should invest the time in reading one (or preferably more) beginning books on the subject.
Assuming that the combination of (person_ows, person_is_owed) is unique
select person_ows,
person_is_owed,
amount,
(select t2.amount
from the_table t2
where (t2.person_ows, t2.person_is_owed) = (t1.person_is_owed, t1.person_ows))
from the_table t1

How can I avoid using multiple SQL calls to get data?

I have a MySQL database, in which I have a table of monkeys:
id name
1 Alice
2 Bill
3 Donkey Kong
4 Edna
5 Feefee
I also have a table of bananas and where they were picked from.
id where_from
1 USA
2 Botswana
3 Banana-land
4 USA
Finally, I have a table matches that describes which bananas belong to which monkeys. Each monkey can only have one banana, and no monkeys can share a banana. Some monkeys may lack a banana.
id monkey_id banana_id
1 3 4
2 4 1
3 5 2
How can I use a single SQL statement to retrieve all the matches? For each match, I want the name of the monkey as well as where the banana is from.
I have tried the following 3 SQL statements, which work:
SELECT * FROM matches
SELECT * FROM monkeys WHERE id=[monkey_id from 1st SQL query]
SELECT * FROM bananas WHERE id=[banana_id from 1st SQL query]
I feel that 3 SQL statements is cumbersome though. Any ideas on how I can just use a single SQL statement? I am just learning SQL and am monkeying around with the basics. Thanks!
Since some monkeys may lack a banana, that implies a LEFT JOIN between matches and monkeys. That will ensure all monkeys are listed, even if they have no bananas in matches.
SELECT
monkeys.name,
bananas.where_from
FROM
monkeys
/* List all monkeys, even if they have no match */
LEFT JOIN matches ON monkeys.id = matches.monkey_id
/* And another LEFT JOIN to link matches to bananas */
LEFT JOIN bananas ON bananas.id = matches.banana_id
Here is an example on SQLfiddle.com
I very highly recommend reading over Jeff Atwood's (co-founder of Stack Overflow) excellent article explaining SQL joins.

Doing a calculation between two rows with EXTRA CRITERIA?

Name ExactDate Presents Location
bob1 2011 1 home
bob2 2008 2 school
bob2 2012 3 school
mary2 1986 4 school
mary1 2001 5 home
mary1 2012 6 home
kate1 2011 7 home
kate1 2012 8 home
kate2 2011 9 school
celia2 2011 10 school
celia2 1986 11 school
celia1 1972 12 home
celia1 2012 14 home
celia2 2012 13 school
This problem is done in SQL in MS Access 2003 for a query.
So the goal is we subtract the amount of presents kate got from celia on the same year ( but since there are a few different present values for the same year we choose to have priority of home > school....for example celia and kate both receive presents in 2012 but celia gets both home presents and school presents in 2012 in which case we choose her home present value to do the calculation) and out put should be something like the following:
Name ExactDate PresentsDiff
celiaminuskate 2011 3
celiaminuskate 2012 6
So far I have :
SELECT 'celiaminuskate'AS [NAME],T1.[date] AS [EXACT DATE],
T1.presents T2.presents AS [PRESENTS DIFF]
FROM Some_Table T1, Some_Table T2
part that I think needs to be fixed??
WHERE (T1.Name = 'celia1'>'celia2')
AND (T2.Name = 'kate1'>'kate2')
AND T2.ExactDate = T1.ExactDate
to indicate priority? I'm not too sure how to do it
ORDER BY T1.ExactDate
I created this query and saved it as qryCeliaAndKateGiftDates. It just returns the distinct ExactDate values for which celia and kate both had gifts recorded.
Notice I re-named your Name field to Recipient, because Name is a reserved word.
SELECT DISTINCT celia.ExactDate
FROM
[SELECT ExactDate
FROM Some_Table
WHERE Recipient Like "celia*"
]. AS celia
INNER JOIN [
SELECT ExactDate
FROM Some_Table
WHERE Recipient Like "kate*"
]. AS kate
ON celia.ExactDate = kate.ExactDate
ORDER BY celia.ExactDate;
Then I used correlated subqueries to return the correct Presents values for celia and kate on each of those ExactDates.
SELECT
raw.recipients AS [NAME],
raw.ExactDate AS [EXACT DATE],
(raw.celia_presents - raw.kate_presents) AS [PRESENTS DIFF]
FROM
[SELECT
'celiaminuskate' AS recipients,
dates.ExactDate,
(SELECT TOP 1 Presents
FROM Some_Table
WHERE
Recipient Like "celia*"
And ExactDate = dates.ExactDate
ORDER BY Location) AS celia_presents,
(SELECT TOP 1 Presents
FROM Some_Table
WHERE
Recipient Like "kate*"
And ExactDate = dates.ExactDate
ORDER BY Location) AS kate_presents
FROM qryCeliaAndKateGiftDates AS dates]. AS raw;
It returns the results you requested when run from Access 2003. However correlated subqueries are notoriously slow, so I'll be interested to see what other answers you get.

SQL Distinct Query for Two Tables

Table1:
id - name - address
-----------------------------
1 - Jim - Some Street
2 - Adam - Some Street
3 - ABC - Some Street
Table2:
id - job - finished_by
---------------------------
1 - ABC - 2
2 - EFD - 3
3 - XYZ - 2
4 - BVC - 1
In the above two tables Table1.id and Table2.finished_by are supposed to be linked.
For, eg in table 2, job ABC was finished by Adam.
My objective is to select DISTINCT records from Table 2.
and the result should output all the job completed by each of the persons.
I have this query so far:
SELECT *
FROM table2
LEFT JOIN table1 ON table2.finished_by = table1.id
LIMIT 0 , 30
This joins the Tables side by side, but how do i edit the query to make it display only distinct records, so that the output is:
id - job - id - name
----------------------------
1 - ABC - 2 - Adam
2 - EFD - 3 - ABC
4 - BVC - 1 - Jim
Update:
So, i've did some googling and made some changes to my query:
SELECT DISTINCT finished_by FROM table2
LEFT JOIN table1 ON table2.finished_by = table1.id
LIMIT 0 , 30
But, it seems that only first line of the query is executed since, i dont see the LEFT JOIN table.
May be this query needs a bit more finishing??
More Updates:
So, from some very distinguished members of StacKOverflow it has been brought to my notice that my logic is totally wrong.. So, i'll try to explain what i am trying to achieve in simple words and not program/code. May be that way i can be fetch a quick solution.
So, there's my Company: CompanyA
people like Jim, Adam etc work for CompanyA.. But, CompanyA sends Jim, Adam etc.. to work for another Company.. Say Company1
Jim, Adam etc can be sent to work for multiple such companies. Say Jim is sent to work for Company1 twice and Adam was sent to work for Company1 thrice.
Table 2 maintains records of how many time a person went to work for Company1 in the following format:
Table2: (Ref: Company1)
id - job - finished_by - Date
------------------------------------
1 - ABC - 2 - 10 Oct
2 - EFD - 3 - 11 Oct
3 - XYZ - 2 - 12 Oct
4 - BVC - 1 - 13 Oct
Now, my objective is simple, The reports need to be generated as follows for Company1:
List the persons we sent to Company1 (in Alphabetic Order)
This list should include No. of times the person went (and Dates)
Should also Include the job he did there while he was working for Company1
For, eg an Ideal Output/Report would be:
Name of Employee - Job Description - Dates
ABC - EFD - 11 Oct
Adam - ABC, XYZ - 10 Oct, 12 Oct
Jim - BVC - 13 Oct
I can do all the basic reporting, But i judt dont know how to Convert the numbers that are sitting into Table2 in finished_by coloumn into their respective names from table1
I hope i'm clear with my question now.
Thanks, Everyone!!
I really appreciate your time and effort
Based on your latest update, it sounds like you want a comma-separated list of the "job" names and dates. MySQL's GROUP_CONCAT function accomplishes that. So perhaps something like this:
SELECT table1.*, GROUP_CONCAT(table2.job), GROUP_CONCAT(table2.date)
FROM table1
INNER JOIN table2 ON (t1.id = t2.finished_by)
GROUP BY t1.id
This will give you a list of all employees who did work, along with comma-separated lists of where they did work and when.
Keep in mind that there's no order to the values in each GROUP_CONCAT list. So you can't be sure, for example, that the first job listed corresponds to the first date listed. But if you wanted to keep that connection intact you'd want each job in a separate row anyway.
SELECT DISTINCT *
FROM table2
LEFT JOIN table1 ON table2.finished_by = table1.id
LIMIT 0 , 30
does this work?
It sounds as though you want all Table1 details, together with a count of all jobs they have finished from Table2. If so, try this:
select t1.id,
max(t1.name) name,
max(t1.address) address,
count(t2.id) finished_jobs
from Table1 t1 left outer join Table2 t2 on t1.id = t2.finished_by
group by t1.id;