How to solve this specific SQL query? - mysql

I am stuck with an this SQL problem: I need to find all members who did not pay their annual fees for 2014. Here is a sample of the database:
Table 'members'
| ID | name |
---------------
| 1 | Franck |
| 2 | Andy |
| 3 | Jack |
Table 'payements'
| ID | memberID | year | amount |
------------------------------------
| 1 | 1 | 2013 | 100 |
| 2 | 1 | 2014 | 100 |
| 3 | 2 | 2013 | 100 |
And I tried something like this:
SELECT members.name FROM members
LEFT JOIN payements ON (payements.memberID = members.ID)
WHERE (payements.year = 2014 AND payements.amount < 100) OR payements.memberID IS NULL
My query correctly finds Jack (who did never pay anything) but fails to find Andy because an entry exists for another year. How can I ask for all members who have no entry specifically for 2014 (or an entry with an amount below 100)?

Consider this data in terms of sets
Set 1 everyone who should have paid
Set 2 people who is paid up correctly
We join the sets together as a left join excluding those who have paid in 2014 from the rest,
we add the limits to the join so that only payments for current year in full are listed. we then exclude those from the complete set of users..
Select m.name, p.memberid, p.year, p.amount
from members m
LEFT JOIN payements p
on m.id = p.memberId
and (p.year = 2014 and p.amount >= 100)
WHERE p.year is null
The reason why your's didn't work was because the where clause was making the outer join an inner join. AND because you wanted a set of users who haven't paid. Not the set who has paid. So we needed to setup the second set as those who have paid... changing < to >=.

Another way using sub-querys in WHERE.
In the sub-query you find all members who DID pay their annual fees. So in the outer query you keep only the members not inside the sub-query, those are the ones you want.
SELECT name
FROM members
WHERE ID NOT IN (SELECT memberID
FROM payements
WHERE year = 2014 AND amount < 100)
BTW, do you mean amount <= 100 ?...
EDIT:
For members who paid their fees in 2014, the amount must be greater or equal than 100, so here is a corrected version:
SELECT name
FROM members
WHERE ID NOT IN (SELECT memberID
FROM payements
WHERE year = 2014 AND amount >= 100)
Added a new member "Amy" in your test, who only paid an amount of 80 in 2014, she is listed with Andy and Jack:
Andy
Jack
Amy
SQL FIDDLE

SELECT * FROM members WHERE ID NOT IN(SELECT memberID FROM payments WHERE year='2014')

Related

mysql result in two columns from same table without duplicate rows

I don't know how to ask this correctly - it was probably asked many times.
I have two tables. Companies and budgets.
Companies [id,name,address]
Budgets [id,company_id,year,budget]
I want to create a select where I would get the result for each company and budgets for each year in the same row.
budgets table:
id | company_id | year | budget
1 | 1 | 2018 | 1500
2 | 1 | 2019 | 2500
3 | 2 | 2018 | 700
4 | 2 | 2019 | 6000
So I would like to get the budgets in year columns
SELECT
a.id,
a.`name`,
IF(b.season_id = 1,b.budget,'') AS "budget 2018",
IF(b.season_id = 2,b.budget,'') AS "budget 2019"
FROM companies AS a
INNER JOIN budgets AS b ON a.id = b.company_id
This of course returns double rows. :)
company name | budget 2018 | budget 2019
company one | 1500 |
company one | | 2500
company two | 700 |
company two | | 6000
How can I get the budgets in the same row?
company name | budget 2018 | budget 2019
company one | 1500 | 2500
company two | 700 | 6000
Or how is this procedure called/described so I can ask uncle google? :)
Edit:
I got suggestions for solving this task with outer joins. Truthfully I haven't had the time to solve the problem with outer joins - not yet confident if I can trust the result. The other thing is that I find the title of the suggested solution somewhat misleading. I ended solving this problem with the use of "fake" aggregates. Because this is a task I need once a year for a custom export I won't be pursuing a "better" solution. Thank you for your input.
With conditional aggregation:
SELECT
c.id,
c.name,
MAX(CASE WHEN b.season_id = 1 THEN b.budget END) AS `budget 2018`,
MAX(CASE WHEN b.season_id = 2 THEN b.budget END) AS `budget 2019`
FROM companies AS c INNER JOIN budgets AS b
ON c.id = b.company_id
GROUP BY c.id, c.name
I use MAX() because in your sample data there is only 1 row for each company/year.
If this is not the case and you want the total for each company/year then use SUM().
You could use a (fake) aggrgation function
SELECT
a.id,
a.`name`,
sum(IF(b.season_id = 1,b.budget,'')) AS `budget 2018`,
sum(IF(b.season_id = 2,b.budget,'')) AS `budget 2019`
FROM companies AS a
INNER JOIN budgets AS b ON a.id = b.company_id
GROUP BY a.id, a.name
probably this is what you wanted to know.
SQL - Select rows from two different tables
use outer join
INNER JOIN budgets AS b ON a.id = b.company_id
to
outer join budgets AS b ON a.id = b.comapny_id

adding rows from the same table that match a criteria in a inner join result

I'm new to programming.
This is assignment work..... AND this is also my first ever SO post !!
Hello world! :)
I've hit a wall trying to use a join/union or ("other" method??) on the results of an inner join query with additional rows from the same table (based on a matching secondary ID).
I'm creating a view with the output.
I have a member table as m
Each member is denoted as part of a family group with family_ID
Each family has a primary contact (primaryContact_ID) person who pays membership fees. membership payments for each primarycontact_ID are in a membershippayment table as mp.
I wish to select all members who are considered paid in a certain year. Note: this means that where the family primary contact has paid, other family members are therefore considered paid / current.
So first I can select paid primary contacts easily. using:
SELECT m.memberNumber, m.firstName, m.lastName, m.family_ID , mp.yearCurrentTo
FROM member m
INNER JOIN membershippayment mp
on m.primaryContact_ID = mp.primaryContact_ID
WHERE mp.yearCurrentTo = 2015
The output is
memberNumber | firstName | lastName | family_ID | yearCurrentTo
6 | ted | smith | 2 | 2015
But Ted has 3 other family members ( also in the members table m) as denoted by family_ID = "2"
How do I (select or join or union or "Other") additional family members with m.family_ID as Ted ?
The family_id value will be dynamic and have 1 or more values, depending on the inner join result set.
i have tried adding an inner join to the original member table using a second alias :
SELECT m.memberNumber, m.firstName, m.lastName, m.family_ID, mp.yearCurrentTo
FROM member m
INNER JOIN membershippayment mp
on m.primaryContact_ID = mp.primaryContact_ID
INNER JOIN member m2
on m2.family_ID = m.family_ID
WHERE mp.yearCurrentTo = 2015
But all I get is Teds details repeated 3 more times.
memberNumber | firstName | lastName | family_ID | yearCurrentTo
6 | ted | smith | 2 | 2015
6 | ted | smith | 2 | 2015
6 | ted | smith | 2 | 2015
6 | ted | smith | 2 | 2015
it should be each additional family member, of unique memberNumber but matching family_ID
I've also tried a union but get similar repeated rows.
From here i'm stumped.
Can it all be achieved in the same query ? I'm thinking using nesting . But I get lost attempting this.
or should I use an updateable view and do a separate select and update query.
Thanks !

Getting COUNT of Specific set of Customers - MySQL Query - Is there a faster way?

I am trying to do a custom report right now. It involves running this query over 50 times for different date conditions.
Anyway, this report revolves around two tables:
agreement
(a list of customer promised to pay - tied to customer table by customer.id = agreement.customer_id)
|----|-------------|---------------------|--------|----------|
| id | customer_id | entered_timestamp | amount | campaign |
|----|-------------|---------------------|--------|----------|
| 1 | 123 | 2015-12-22 13:12:00 | 30 | 'xyz' |
|----|-------------|---------------------|--------|----------|
| 2 | 400 | 2015-12-22 13:15:00 | 20 | 'abc' |
|----|-------------|---------------------|--------|----------|
previous_customer_ids
(a list of customer ids that have at least one paid agreement - tied to customer table by customer.id = previous_customer_ids.customer_id)
|----|-------------|
| id | customer_id |
|----|-------------|
| 1 | 123 |
|----|-------------|
I am trying to get a count of all unique customer_ids whose most recent agreement was in jan or july for a certain campaign and also exist in previous_customer_ids.
I was able to figure out how to get a list of each customer's most recent agreement who exists in previous_customer_ids, and get a count of that number of customers.
However, the query takes 35 seconds to run. I have to run it 60 times over each time this report is pulled (using php to display the results).
select count(t1.customer_id)
from agreement t1
inner join (
select customer_id, max(entered_timestamp) as latestOrder
from agreement
where campaign = 'vsf'
group by customer_id
) t2
inner join previous_customer_ids pcids
on t1.customer_id = pcids.customer_id
where t1.customer_id = t2.customer_id
AND t1.entered_timestamp= t2.latestOrder
AND (substr(t1.entered_timestamp,6,2) = '01'
OR substr(t1.entered_timestamp,6,2) = '07')
How to optimize this?

Calculating values in a hierarchy of a known depth

I have to query a few derived values from 2 tables. Their simplified structure is as follows:
Users
Users have a ID and a parent column which denotes the ID of their parent. Each user also has a commission value which denotes what % of sales that they get from the employees in their line.
Only employees can make sales and that information is recorded in the next table
+------------------------------------+
| ID Name Parent Commission |
+------------------------------------+
| 1 SeniorManager NULL 5 |
| 2 Manager 1 10 |
| 3 Employee1 2 13 |
| 4 Employee2 2 12 |
+------------------------------------+
Sales
This table records the sales from the employees linked through their ID. It records the sale amount as well as when the sale was made.
+---------------------------+
| user_id amount created_at |
+---------------------------+
| 3 100 2014-01-16 |
| 3 120 2014-01-16 |
| 3 110 2014-01-16 |
+---------------------------+
From the other parts of the system, I know the depth of a user given his ID. In the actual system, there is 7 fixed levels but I am simplifying it here for the sake of the question.
The query that I am trying to write is that: Given the ID of a SeniorManager and a date range, show a list of managers under him, the aggregated commissions of those managers as well as the commission expected from that manager. So given the data above one would expect:
+--------------------------------------------+
| Name Sales ManagerCommission Commission |
+--------------------------------------------+
| Manager 330 33.30 16.65 |
+--------------------------------------------+
The query I have so far is:
SELECT
users.name AS Name,
SUM(sales.amount) AS Sales,
SUM(sales.amount) * (users.commission/100) AS ManagerCommission
FROM
users
LEFT JOIN users AS employees
ON employees.parent = users.id
LEFT JOIN sales
ON sales.id = employees.id AND
sales.created_at BETWEEN DATE(?) AND DATE(?)
WHERE
users.parent = ?
GROUP BY
users.name
I am unsure how to get that last column value of the commission grouped by managers instead of employees. Also as a side question, is there a way to reuse the SUM(sales.amount) which is used twice in the select statement. I would rather not calculate the exact same value twice. I am planning on writing 7 queries, for each of the known depths. Is there a more efficient way of doing this?
Adding one join for each level of management:-
SELECT
senior_manager.name AS Name,
SUM(sales.amount) AS Sales,
SUM(sales.amount * manager.commission/100) AS ManagerCommission,
SUM(sales.amount) * (senior_manager.commission/100) AS SeniorManagerCommission
FROM
users AS senior_manager
LEFT JOIN users AS manager
ON manager.parent = senior_manager.id
LEFT JOIN users AS employees
ON employees.parent = manager.id
LEFT JOIN sales
ON sales.id = employees.id AND
sales.created_at BETWEEN DATE(?) AND DATE(?)
WHERE
senior_manager.id = ?
GROUP BY
senior_manager.name
SQL fiddle for it:-
http://www.sqlfiddle.com/#!2/2cfffe/4

What SQL query would I run to get a list of auctions I'm losing? (eBay example)

I'll try to give an "eBay" example, I'm sure it will be the easiest way to ask this.
Imagine a table of bids:
bid_id | bid_from_user | bid_for_auction | bid_price
1 | 150 | 1 | 20
2 | 453 | 1 | 30
3 | 42 | 1 | 50
4 | 12 | 2 | 12
5 | 60 | 2 | 20
Imagine you need to list auctions which a certain person is currently bidding on and losing.
Therefore, our current user ID is 12, so bid_from_user='12'
This user is currently bidding on auction id 2 with price 12 and is obviously losing, as there is another user bidding on the same auction with a higher price.
I don't want to list all the auctions the user is bidding on, I just want to list those the user is bidding on and losing (= not the ones he's winning).
So, what query do I run to do this?
SELECT b.bid_id, b.bid_from_user, b.bid_for_auction, b.bid_price
FROM bids b
WHERE b.bid_from_user = '12'
AND EXISTS (
SELECT 1
FROM bids x
WHERE x.bid_for_auction = b.bid_for_auction
AND x.bid_price > b.bid_price
AND x.bid_from_user <> b.bid_from_user);
Edit: The above would seem to be the cleanest, most legible solution; but here is an alternative in allowance for MySQL traditionally not handling subqueries well...
SELECT b.bid_for_auction
FROM bids b
JOIN bids x ON x.bid_for_auction = b.bid_for_auction
AND x.bid_price > b.bid_price
AND x.bid_from_user <> b.bid_from_user
WHERE b.bid_from_user = '12'
GROUP BY b.bid_for_auction;
The values in the select are just for proof:
SELECT MAX( a.bid_price ), a.bid_for_auction, b.bid_price
FROM bids a
INNER JOIN bids b ON a.bid_for_auction = b.bid_for_auction
WHERE b.bid_from_user = 12
GROUP BY bid_for_auction
HAVING b.bid_price < MAX(a.bid_price);