Column Relationships - sqlite

I realize i'm far off the solution with what i have:
Select FirstName || ' ' || LastName AS Manager From Employee
Where (Select COUNT(ReportsTo) from Employee
group by ReportsTo
order by ReportsTo desc);
ReportsTovalues are the EmployeeID they report to
What i want is to query the name of the employee with the most Employees reporting to them and who they in turn report to without nulls. I'm Not sure how to make the connections between columns values such as ReportsTo to EmployeeID so any explanation would help
For Example the output i would want is two columns say | Fred Jones | Mary Anne| the first being the employee with the most reportsTo with the same value as their EmployeeID and the second being the name of the employee with the same EmployeeID as the first employees ReportTo

Do this step by step:
First step: Count how many employees report to a person.
select reportsto, count(*) from employee group by reportsto;
We can order this result by count(*) and limit it to only get one row, so as to get the person with the most reporters. Only problem is: What to do in case of ties, i.e. two persons have the same highest amount of reporters? SQLite doesn't offer much to help here. We'll have to query twice:
select reportsto
from employee
group by reportsto
having count(*) =
(
select count(*)
from employee
group by reportsto
order by count(*) desc
limit 1
);
Next step: Get the name. That means we must access the table again.
select
firstname || ' ' || lastname as manager
from employee
where e1.employeeid in
(
select reportsto
from employee
group by reportsto
having count(*) =
(
select count(*)
from employee
group by reportsto
order by count(*) desc
limit 1
)
);
Last step: Get the persons our found managers themselves report to. These can be many, so we group by manager and concatenate all those they report to.
select
e1.firstname || ' ' || e1.lastname as manager,
group_concat(e2.firstname || ' ' || e2.lastname) as reportsto
from employee e1
join employee e2 on e2.employeeid = e1.reportsto
where e1.employeeid in
(
select reportsto
from employee
group by reportsto
having count(*) =
(
select count(*)
from employee
group by reportsto
order by count(*) desc
limit 1
)
)
group by e1.firstname || ' ' || e1.lastname;

SELECT e.ReportsTo AS TopManagersEmployeeId, COUNT(e.ReportsTo) AS ReportedBy, m.FirstName + ' ' + m.LastName AS TopManagersName, mm.FirstName + ' ' + mm.LastName AS TheirManagersName FROM Employees e
JOIN Employees m
ON e.ReportsTo = m.EmployeeID
JOIN Employees mm
ON m.ReportsTo = mm.EmployeeID
GROUP BY e.ReportsTo, m.FirstName, m.LastName, mm.FirstName, mm.LastName
Once you have this data, you can do TOP 1 etc. You can also play around with JOIN, and make it INNER JOIN in the second set where Manager's Manager (mm) is being retrieved.

Related

How do I include all max values within a row?

I'm very new to learning SQL, I apologize if my question isn't completely accurate.
The question I'm trying to answer with this query is "What is the most popular music genre in each country?" I've had to use a subquery and it works, but I found that for a few countries in the table, more than one genre has the MAX value. I'm stuck with how to edit my query so that all genres with the max value show in the results. Here is my code, using DB Browser for SQLite:
SELECT BillingCountry AS Country , name AS Genre , MAX(genre_count) AS Purchases
FROM (
SELECT i.BillingCountry, g.name, COUNT(g.genreid) AS genre_count
FROM Invoice i
JOIN InvoiceLine il
ON il.InvoiceId = i.InvoiceId
JOIN TRACK t
ON il.trackid = t.TrackId
JOIN Genre g
ON t.genreid = g.GenreId
GROUP BY 1,2
) sub
GROUP BY 1
Here is an example of the result:
| Country | Genre |Purchase|
|---------|-------|--------|
|Agrentina| Punk | 9 |
|Australia| Rock | 22 |
BUT in running just the subquery to COUNT the purchases, Argentina has two Genres with 9 Purchases (the max number for that country). How do I adjust my query to include both and not just the first one in the row?
You can do it with RANK() window function:
SELECT BillingCountry, name, genre_count
FROM (
SELECT i.BillingCountry, g.name, COUNT(*) AS genre_count,
RANK() OVER (PARTITION BY i.BillingCountry ORDER BY COUNT(*) DESC) rnk
FROM Invoice i
INNER JOIN InvoiceLine il ON il.InvoiceId = i.InvoiceId
INNER JOIN TRACK t ON il.trackid = t.TrackId
INNER JOIN Genre g ON t.genreid = g.GenreId
GROUP BY i.BillingCountry, g.name
)
WHERE rnk = 1
This will return the ties in separate rows.
If you want 1 row for each country, you could also use GROUP_CONCAT():
SELECT BillingCountry, GROUP_CONCAT(name) AS name, MAX(genre_count) AS genre_count
FROM (
SELECT i.BillingCountry, g.name, COUNT(*) AS genre_count,
RANK() OVER (PARTITION BY i.BillingCountry ORDER BY COUNT(*) DESC) rnk
FROM Invoice i
INNER JOIN InvoiceLine il ON il.InvoiceId = i.InvoiceId
INNER JOIN TRACK t ON il.trackid = t.TrackId
INNER JOIN Genre g ON t.genreid = g.GenreId
GROUP BY i.BillingCountry, g.name
)
WHERE rnk = 1
GROUP BY BillingCountry

SQLITE - Using results from a subquery in same query

I would like to know if, and if yes, how I could accomplsh the following:
Lets say I have two tables:
Table A has two Columns: id, name
Table B columns: owner, argument
Now I am trying to find in table A all rows with specific name (animal) and use their ids to find it's argument value in table b. Those argument values are different ids in table a. So as a result I would like to get two columns. first has the id of the items who has the specific name (animal) I am looking for and second column has the name of the item which has the id that is argument of the initial ids.
table a (example)
id || name
1 || animal
2 || animal
3 || animal
4 || animal
15 || cat
16 || dog
17 || horse
18 || bird
...
table b (example)
owner || argument
1 || 15
2 || 16
3 || 17
4 || 18
...
result (example)
id || name
1 || cat
2 || dog
3 || horse
4 || bird
Thanks in advance for any hints / help.
Andreas
You need a double join from tablea to tableb and again doublea:
select
a.name ownwename,
t.name name
from tablea a
inner join tableb b
on b.owner = a.id
inner join tablea t
on t.id = b.argument
where a.name = 'animal'
See the demo
I believe the following will do what you want
SELECT owner, name FROM tableb JOIN tablea ON argument = id;
However, as using a subquery you could use :-
SELECT owner, (SELECT name FROM tablea WHERE argument = id) AS name FROM tableb;
Working Example :-
DROP TABLE If EXISTS tablea;
CREATE TABLE IF NOT EXISTS tablea (id INTEGER PRIMARY KEY, name TEXT);
INSERT INTO tablea (name) VALUES ('animal'),('animal'),('animal'),('animal'),('cat'),('dog'),('horse'),('bird'),
('animal'),('cat'),('dog'),('horse'),('bird'),('animal'),
('cat'),('dog'),('horse'),('bird') -- id's 15-18 inclusive
;
DROP TABLE IF EXISTS tableb;
CREATE TABLE IF NOT EXISTS tableb (owner INTEGER PRIMARY KEY, argument INTEGER);
INSERT INTO tableb (argument) VALUES(15),(16),(17),(18);
SELECT owner, name FROM tableb JOIN tablea ON argument = id;
SELECT owner, (SELECT name FROM tablea WHERE argument = id) AS name FROM tableb;
Results :-
and the second

SQL query to update value in table to sum of attribute in another table, where the ID's match

I have tried several different queries and cannot seem to find one that works without causing an error
Incorrect syntax near keyword ' '
I have one table called Scores that saves the userID, week number, and that weeks score from a quiz.
Schema:
Scores (Id, userName, weekNumber, currentScore)
There are 12 weeks total, so in the end each user will have 12 entries in this table
I have another table Leaderboard that has schema :
Leaderboard (Id, userName, week1, week2, week3 ..... week12, totalScore)
Each user will only have one entry in this table.
I have been trying to save the sum of the currentScore from Scores into the totalScore attribute of Leaderboard and cannot seem to figure out the correct syntax.
My query is as follows:
UPDATE t1
SET t1.totalScore = t2.completeScore
FROM dbo.Leaderboard AS t1,
((SELECT Id, SUM(weeklyScore) AS completeScore FROM dbo.Scores) as F
INNER JOIN
(SELECT Id FROM dbo.Scores GROUP BY Id) AS S ON F.Id = S.Id) AS t2
WHERE t1.Id = t2.Id
Try this and let us know how it goes:
var total1 = ("UPDATE t1 " +
"SET t1.totalScore = t2.completeScore " +
"FROM dbo.Leaderboard AS t1, (SELECT F.Id, F.completeScore FROM " +
"(SELECT Id, SUM(weeklyScore) AS completeScore FROM dbo.Scores GROUP BY ID) as F " +
"INNER JOIN (SELECT Id FROM dbo.Scores/* GROUP BY Id*/) AS S "+
"ON F.Id = S.Id) AS t2 "+
"WHERE t1.Id = t2.Id");
By the way I think you were missing a GROUP BY clause in the definition of the F derived table and that you don't need the GROUP BY in the definition of the S derived table.

Counting 2 Columns from Separate Tables Between 2 Dates

I have a query that Counts 2 columns from 2 separate tables using subqueries, which works. Now I have to implement into this query the ability to filter out these results based on the Date of a Call Record. I will post the query in which I am working with:
SELECT (m.FirstName || " " || m.LastName) AS Members,
(
SELECT count(CallToLineOfficers.MemberID)
FROM CallToLineOfficers
WHERE CallToLineOfficers.MemberID = m.MemberID
)
+ (
SELECT count(CallToMembers.MemberID)
FROM CallToMembers
WHERE CallToMembers.MemberID = m.MemberID
) AS Tally
FROM Members AS m, Call, CallToMembers, CallToLineOfficers
Join Call on CallToMembers.CallID = Call.CallID
and CallToLineOfficers.CallID = Call.CallI
WHERE m.FirstName <> 'None'
-- and Call.Date between '2017-03-21' and '2017-03-22'
GROUP BY m.MemberID
ORDER BY m.LastName ASC;
Ok, so table Call stores the Date and its PK is CallID. Both CallToLineOfficers and CallToMembers are Bridge Tables that also contain only CallID and MemberID. With the current query, where the Date is commented out, that Date range should only return all names, but a count of 1 should appear under 1 person's name.
I have tried joining Call.CallID with both Bridge Tables' CallIDs without any luck, though I think this is the right way to do it. Could someone help point me in the right direction? I am lost. (I tried explaining this the best I could, so if you need more info, let me know.)
UPDATED: Here is a screenshot of what I am getting:
Based on the provided date in the sample, the new results, with the Date, should be:
Bob Clark - 1
Rob Catalano - 1
Matt Butler - 1
Danielle Davidson - 1
Jerry Chuska - 1
Tom Cramer - 1
Everyone else should be 0.
At the moment, the subqueries filter only on the member ID. So for any member ID in the outer query, they return the full count.
To reduce the count, you have to filter in the subqueries:
SELECT (FirstName || " " || LastName) AS Members,
(
SELECT count(*)
FROM CallToLineOfficers
JOIN Call USING (CallID)
WHERE MemberID = m.MemberID
AND Date BETWEEN '2017-03-21' AND '2017-03-22'
)
+ (
SELECT count(*)
FROM CallToMembers
JOIN Call USING (CallID)
WHERE MemberID = m.MemberID
AND Date BETWEEN '2017-03-21' AND '2017-03-22'
) AS Tally
FROM Members AS m
WHERE FirstName <> 'None'
ORDER BY LastName ASC;

trigger insert different row

The sqlite3 trigger that I want to create might or might not be possible with sql. Five tables are involved:
Members Groups GroupMembers Accounts
mId |name| accId gId | name | accId gId | mId accId | balance
Orders
oId | accId | ammount
When someone deletes a group, I want to make an order for each of the group members with the average of the group balance. So it should do something like this:
CREATE NEW TRIGGER triggername
BEFORE DELETE ON Groups
WHEN ((SELECT balance FROM Accounts WHERE accId=OLD.accId) = 0)
FOR EACH ROW IN
(SELECT accId
FROM GroupMembers JOIN Members ON GroupMembers.mId = Members.mId
WHERE GroupMembers.gId = OLD.gId)
BEGIN
INSERT INTO Orders(accId,ammount) VALUES(accId,
(SELECT balance FROM Accounts WHERE accId = OLD.accId)
/
(SELECT SUM(mId) FROM GroupMembers WHERE gId = OLD.gId)
);
END
The question is: is it possible to create a FOR EACH ROW in any other table than the table at which the trigger applies? Is it possible to put the WHEN statement before the FOR EACH statement?
As documented, there is no such a thing as a FOR EACH ROW IN ... clause.
However, the INSERT statement can use a SELECT statement as the source of the data to be inserted.
Write a SELECT statement that returns one row for each group member:
CREATE TRIGGER triggername
AFTER DELETE ON Groups
FOR EACH ROW
BEGIN
INSERT INTO Orders(accId, amount)
SELECT accId,
(SELECT balance
FROM Accounts
WHERE accId = OLD.accId) /
(SELECT COUNT(*)
FROM GroupMembers
WHERE gId = OLD.gId)
FROM Members
WHERE mId IN (SELECT mId
FROM GroupMembers
WHERE gId = OLD.gId);
END;
(I dropped the balance = 0 filter.)

Resources