Finding many matches to one row in the same table in mysql - mysql

My table is this.
users_table:
id | name | admin | property_id
-----------------------------------
1 | x | 1 | 0
2 | y | 1 | 0
3 | z | 0 | 1
4 | t | 0 | 2
4 | u | 0 | 2
4 | o | 0 | 2
I have two records which are admin and some other records which belong to one of these two records by matching the property_id with the id.
In the end what I want is the admin row data and the count of its properties.
The problem is that the data is all in the same table.
This is what should be the output from the desired query.
id | name | admin | property_count
-----------------------------------
1 | x | 1 | 1
2 | y | 1 | 3

http://sqlfiddle.com/#!9/5ad1fb/4
SELECT u.*, COUNT(ut.id) property_count
FROM users_table u
LEFT JOIN users_table ut
ON u.id = ut.property_id
WHERE u.admin = 1
GROUP BY u.id, u.name, u.admin

You seem to want a self-join and aggregation:
select t1a.id, t1a.name, t1a.admin, count(t1.id) as property_count
from table1 t1a left join
table1 t1
on t1a.id = t1.property_id
where t1a.admin = 1
group by t1a.id, t1a.name, t1a.admin;
There is, incidentally, a trickier way to do this without a join:
select (case when admin = 1 then id else property_id end) as id,
max(case when admin = 1 then name end) as name,
max(admin) as admin,
sum( admin <> 1 ) as property_count
from table1 t1
group by (case when admin = 1 then id else property_id end);

Related

SQL Difference between two row in group by

I have a table records of store id, processing batch id and start time as follows:
|store_id | batch_id | process_start_time |
| A | 1 | 10 |
| B | 1 | 40 |
| C | 1 | 30 |
| A | 2 | 400 |
| B | 2 | 800 |
| C | 2 | 600 |
| A | 3 | 10 |
| B | 3 | 80 |
| C | 3 | 90 |
Here, rows needed to be grouped by batch_id and time_taken is difference of process_start_time of store A and store C.
So, the expected result would be:
batch_id | time_taken
1 | 20
2 | 200
3 | 80
I tried to do something like:
select batch_id, ((select process_start_time from records where store_id = 'C') - (select process_start_time from records where store_id = 'A')) as time_taken
from records group by batch_id;
But couldn't figure out to select specific rows in that particular group.
Thank you for looking into!
Update: the process_start_time column not necessarily max for store C
You seem to want conditional aggregation and arithmetic:
select batch_id,
(max(case when store_id = 'C' then process_start_time end) -
min(case when store_id = 'A' then process_start_time end)
) as diff
from records
group by batch_id;
You can try a self join.
SELECT r1.batch_id,
r1.process_start_time - r2.process_start_time time_taken
FROM records r1
INNER JOIN records r2
ON r1.batch_id = r2.batch_id
WHERE r1.store_id = 'C'
AND r2.store_id = 'A';
Here's another answer. This is using two instances of the records table and we link them up with where clauses and exists as follows:
select a.batch_id,
c.process_start_time - a.process_start_time as time_taken
from records a,
records c
where a.store_id = 'A'
and c.store_id = 'C'
and exists (
select 1
from records x
where x.batch_id = a.batch_id
and x.batch_id = c.batch_id
);
SELECT DISTINCT
store_a.batch_id,
store_c.process_start_time - store_a.process_start_time AS 'time_taken'
FROM records store_a
INNER JOIN records store_c
ON store_a.batch_id = store_c.batch_id
AND store_c.store_id = 'C'
AND store_a.store_id = 'A'

show result of one table and find in other column

let me show table structure first then my question
suppose table A
id | url |
1 | page
2 | home
3 | product
4 | sell
and table b
id |user_id | table a id
1 | 1 | 1
the output which I want like
id | url | ispermission
1 | page | 1
2 | home | 0
3 | product | 0
4 | sell | 0
(value 1 )in ispermission mean that it map with user as you can see in table b other mean it does not have mapping
below is my query
select p.id,p.url,if(r.user_id=4,1,0) ispermission from
`table a` p
left join `table b` r on p.id=r.url_id
group by p.id,p.url order by ispermission DESC
this query is working great but in some case it got failed the result let conside that failed case the output which i m getting like (note this result is without groupby)
id | url | ispermission
1 | page | 0
1 | page | 1
2 | home | 0
3 | product | 0
4 | sell | 0
by using group by i m getting result like
id | url | ispermission
1 | page | 0
2 | home | 0
3 | product | 0
4 | sell | 0
Are you looking for something like this?
SELECT
T1.ID,
T1.Url,
COUNT(T2.[table a id]) Impression
FROM TableA T1
LEFT JOIN TableB T2 ON T1.ID = T2.[table a id]
GROUP BY T1.ID,T1.Url
Using LEFT JOIN with CASE expression:
select a.id, a.url,
case when b.tableAid is not null then 1 else 0 end as ispermission
from TableA a
left join TableB b on b.tableAid = a.id
Demo on db<>fiddle

Select values if doesn't match in other table by left join

I am having some problem with getting the values from two tables of my database .
I have two tables in my database 1 is mem and 2nd is payment
mem stores name and drawid of users
payment table stores draw and instalment of the user
User pays us every month .
so if a user with a draw id 1 pays us in feb the values in two tables are
mem drawid=1 and name = something
payment draw = 1 and instalment=2
drawid in mem is same as draw in payment
so the tables has many to many relation.
Now I need to find the list of all the members who have not paid even 1 instalment before 4th month.
I am using this query
SELECT drawid,contact,dnd,mem.name, count(*) as numPayments FROM mem
LEFT JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPayments < 4
it's working all good no issues only the issue is that I also need to show the instalments the user has paid so I need to fetch all the instalment from table payments and then show by while loop.
This query is perfect but it gives me repeated results !!!
SELECT drawid,contact,dnd,mem.name, count(*) as numPayments,NULL numPaidPayments ,NULL PAID_CONTACT,NULL NAME_PAID FROM mem
LEFT JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPayments < 4
UNION
SELECT NULL drawid,NULL contact, NULL dnd, NULL name,NULL numPayments,COUNT(*) as numPaidPayments ,contact PAID_CONTACT,mem.name NAME_PAID FROM mem
INNER JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPaidPayments >= 4
SELECT drawid,contact,dnd,mem.name, count(*) as numPayments,NULL numPaidPayments ,NULL PAID_CONTACT,NULL NAME_PAID FROM mem
LEFT JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPayments < 4
UNION
SELECT NULL drawid,NULL contact, NULL dnd, NULL name,NULL numPayments,COUNT(*) as numPaidPayments ,contact PAID_CONTACT,mem.name NAME_PAID FROM mem
INNER JOIN payment ON (mem.drawid = payment.draw) GROUP BY
drawid HAVING numPaidPayments >= 4
Try above query.
Given this
MariaDB [sandbox]> select * from member;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
+------+
4 rows in set (0.00 sec)
MariaDB [sandbox]> select * from payment;
+--------+------+
| mem_id | mth |
+--------+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 1 |
| 2 | 3 |
| 3 | 2 |
| 3 | 4 |
+--------+------+
8 rows in set (0.00 sec)
This
select m.id, group_concat(p.mth order by p.mth) mthspaid,
sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsPaid,
4 - sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsMissed
from member m
left join payment p on p.mem_id = m.id
group by m.id
Gives this
+------+----------+--------------+----------------+
| id | mthspaid | NoofMthsPaid | NoofMthsMissed |
+------+----------+--------------+----------------+
| 1 | 1,2,3,4 | 4 | 0 |
| 2 | 1,3 | 2 | 2 |
| 3 | 2,4 | 2 | 2 |
| 4 | NULL | 0 | 4 |
+------+----------+--------------+----------------+
4 rows in set (0.00 sec)
And if you add in code to work out the months due
select paid.*, due.mthsmissed
from
(
select m.id, group_concat(p.mth order by p.mth) mthspaid,
sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsPaid,
4 - sum(case when p.mem_id is not null then 1 else 0 end) NoofMthsMissed
from member m
left join payment p on p.mem_id = m.id
group by m.id
) paid
left join
(
select due.id, group_concat(due.mth order by due.mth) MthsMIssed
from
(
select m.id,d.mth
from member m,(select 1 mth union select 2 union select 3 union select 4) d
) due
left join payment p on p.mem_id = due.id and p.mth = due.mth
where p.mth is null
group by due.id
) due on paid.id = due.id
You get this
+------+----------+--------------+----------------+------------+
| id | mthspaid | NoofMthsPaid | NoofMthsMissed | mthsmissed |
+------+----------+--------------+----------------+------------+
| 1 | 1,2,3,4 | 4 | 0 | NULL |
| 2 | 1,3 | 2 | 2 | 2,4 |
| 3 | 2,4 | 2 | 2 | 1,3 |
| 4 | NULL | 0 | 4 | 1,2,3,4 |
+------+----------+--------------+----------------+------------+
4 rows in set (0.04 sec)

MySQL vote history for user with total vote count for each item

I have tables similar to these:
names (these are being voted on)
+----+-------+
| ID | name |
+----+-------+
| 1 | name1 |
| 2 | name2 |
| 3 | name3 |
| 4 | name4 |
+----+-------+
votes
+----+----------+------+---------+
| ID | voter_id | time | name_id |
+----+----------+------+---------+
| 1 | 1 | x | 1 |
| 2 | 2 | x | 1 |
| 3 | 3 | x | 2 |
| 4 | 4 | x | 2 |
| 5 | 5 | x | 4 |
+----+----------+------+---------+
expected result for voter_id 1
| n.ID | n.name | v.time | votecount|
+------+--------+--------+----------+
| 1 | name1 | x | 2 |
+------+--------+--------+----------+
The query I'm using:
SELECT COUNT(DISTINCT v.VOTE_ID) VOTECOUNT, v.VOTE_TIME, v.VOTER_ID, n.ID, n.NAME
FROM `votes` as v
LEFT JOIN `names` as n
ON n.ID = v.NAME_ID
WHERE v.VOTER_ID = :id
GROUP BY n.ID
ORDER BY v.TIME DESC
Alternate query:
SELECT COUNT(DISTINCT v.VOTE_ID) VOTECOUNT, v.VOTE_TIME, v.VOTER_ID, n.ID, n.NAME
FROM `votes` as v
LEFT JOIN `names` as n
ON n.ID = v.NAME_ID
GROUP BY n.ID
HAVING v.VOTER_ID = :id
ORDER BY v.TIME DESC
I'm trying to get a list of items that user has voted for and also count how many votes in total each item has gotten.
If I use the WHERE clause the votecount is obviously always 1, but it gets all items the user has voted for.
I've tried using HAVING voter_id to keep the votecount total and that sort of works, but then some items the user has voted on are omitted for some reason. Changing LEFT JOIN for RIGHT JOIN would give a slightly different list, but never complete.
Would there be some better way of doing this?
Two things.
First, try replacing your HAVING clause in the second query with HAVING MAX(CASE WHEN v.VOTER_ID = :id THEN 1 ELSE 0 END) = 1.
Second, your select list doesn't match up with your group by, so I'm not sure what you're trying to do there. What is the time column exactly? My attempt at what you want would look something like:
SELECT COUNT(DISTINCT v.VOTE_ID) VOTECOUNT, n.ID, n.NAME
FROM `votes` as v
LEFT JOIN `names` as n
ON n.ID = v.NAME_ID
GROUP BY n.ID, n.NAME
HAVING MAX(CASE WHEN v.VOTER_ID = :id THEN 1 ELSE 0 END) = 1

How to retrieve the most present pair in groups of three columns, with MySQL?

I am working on ice hockey software: trying to find out who in your team has collected the most points with a specific player (in this example user_id = 1).
Data structure:
goal_user_id | assist_user_id | second_assist_user_id
-----------------------------------
1 | 13856 | null
1 | 15157 | null
1 | 15157 | null
1 | 15157 | 18733
345 | 1 | 28703
18733 | 1 | null
36014 | 34867 | 1
Desired output:
user_id | partner_id | total_points
-----------------------------------
1 | 15157 | 3
1 | 18733 | 2
1 | 13856 | 1
1 | 345 | 1
1 | 28703 | 1
1 | 34867 | 1
1 | 36014 | 1
SQL Fiddle:
http://sqlfiddle.com/#!9/b1587/4/0 (Note: SQLFiddle has been acting weird today).
I managed to get it done on the across two columns but couldn't figure out for three:
SELECT
COUNT(goal_user_id) as total_points,
CASE WHEN goal_user_id <> 1
THEN goal_user_id
ELSE assist_one_user_id
END as partner_id,
CASE WHEN goal_user_id < assist_one_user_id
THEN CONCAT(goal_user_id,'-',assist_one_user_id)
ELSE CONCAT(assist_one_user_id,'-',goal_user_id)
END as player_pair
FROM goals
WHERE
assist_one_user_id IS NOT NULL AND (goals.goal_user_id = 1 OR goals.assist_one_user_id = 1)
GROUP BY player_pair
ORDER BY total_points DESC
My inclination is to break all the rows out into pairs of users. Then do the aggregation based on that:
select u2, count(*) as total_points
from ((select goal_user_id as u1, assist_one_user_id as u2 from goals) union all
(select assist_one_user_id, goal_user_id from goals) union all
(select goal_user_id, assist_two_user_id from goals) union all
(select assist_two_user_id, goal_user_id from goals) union all
(select assist_one_user_id, assist_two_user_id from goals) union all
(select assist_two_user_id, assist_one_user_id from goals)
) uu
where uu.u1 = 1 and uu.u2 is not null
group by u2
order by total_points desc;
Here is a SQL Fiddle.