How to select multiple tables? with 6 tables - mysql

I have a 6 tables
table countries
country_id | short_name
table user (if status = 2 active if status = 1 inactive)
id | country_id | status
table user_subscribed_disciplines
id | user_id |discipline_id
table disciplin
id | name
table user_subscribed_study_levels
id | user_id | study_level_id
table study_levels
id | name
How to select all countries with his parameters? How to write right sql query?
country | total users count | active users count | inactive users count | discipline subscribers count | top discipline | study level subscribers count | top study level
USA | 506 | 500 | 6 | 50 | PHD | 90 | Social History
CAN | 406 | 406 | 0 | 50 | POS | 0 | Social History

You are looking for a query like below I think:
select c.country_id
, count(u.id) as TotalUsers
, sum(case when u.status = 'active' then 1 else 0 end) as ActiveUsers
, sum(case when u.status = 'Inactive' then 1 else 0 end) as InactiveUsers
, (select count(*) from user_subscribed_disciplines) as DisciplineSubscribers
, (select name from (select top 1 name,count(*) from disciplines group by name order by count(*) desc) t) as TopDiscipline
, (select count*) from user_subscribed_study_levels) as StudyLevelCount
, (select name from (select top 1 name,count(*) from study_levels group by name order by count(*) desc) t) as TopStudyLevel
from countries c
inner join user u on u.country_id=c.country_id

Related

How do i add another COUNT statement with different condition to sql query

I have 2 tables.
1st table: duels
| duelId | user1Id | user2Id | gameId | winnerId |
2nd table: usergameprogress
| usergameprogressId | userId | gameId | gameStar |
Given an userId, I would like to get duel count, gameStar, win count for each gameId.
Example return:
| duelCount | duelWinCount | gameStar | gameId |
I have managed to get duelCount, gameStar and gameId given a userId but I couldn't add duelWinCount to my query result. How do I do that ?
My query:
SELECT
COUNT(d1.duelId) AS duelCount,
usergameprogress.gameId, usergameprogress.gameStar
FROM
duels d1
JOIN
usergameprogress ON (usergameprogress.gameId = d1.gameId)
WHERE
d1.user1Id = "gkfurcwsi033qzxg0u2bmj1ekebsklej"
OR d1.user2Id = "gkfurcwsi033qzxg0u2bmj1ekebsklej"
GROUP BY
usergameprogress.gameId
EDIT:
solved thanks to comment use sum instead of count
SELECT sum(case when d1.user1Id = 'gkfurcwsi033qzxg0u2bmj1ekebsklej' OR d1.user2Id="gkfurcwsi033qzxg0u2bmj1ekebsklej" then 1 else 0 end) AS totalDuelCount,sum(case when winnerId="gkfurcwsi033qzxg0u2bmj1ekebsklej" then 1 else 0 end) AS duelWinCount,usergameprogress.gameId,usergameprogress.gameStar FROM duels d1 JOIN usergameprogress ON (usergameprogress.gameId = d1.gameId) GROUP BY usergameprogress.gameId

select two tables mysql without join

There are two tables, recharge and purchase.
select * from recharge;
+-----+------+--------+---------------------+
| idx | user | amount | created |
+-----+------+--------+---------------------+
| 1 | 3 | 10 | 2016-01-09 20:16:18 |
| 2 | 3 | 5 | 2016-01-09 20:16:45 |
+-----+------+--------+---------------------+
select * from purchase;
+-----+------+----------+---------------------+
| idx | user | resource | created |
+-----+------+----------+---------------------+
| 1 | 3 | 2 | 2016-01-09 20:55:30 |
| 2 | 3 | 1 | 2016-01-09 20:55:30 |
+-----+------+----------+---------------------+
I want to figure out balance of users which is SUM(amount) - COUNT(purchase.idx). (in this case, 13)
So I had tried
SELECT (SUM(`amount`)-COUNT(purchase.idx)) AS balance
FROM `recharge`, `purchase`
WHERE purchase.user = 3 AND recharge.user = 3
but, it returned error.
If you want an accurate count, then aggregate before doing arithmetic. For your particular case:
select ((select sum(r.amount) from recharge where r.user = 3) -
(select count(*) from purchase p where p.user = 3)
)
To do this for multiple users, move the subqueries to the from clause or use union all and aggregation. The second is safer if a user might only be in one table:
select user, coalesce(sum(suma), 0) - coalesce(sum(countp), 0)
from ((select user, sum(amount) as suma, null as countp
from recharge
group by user
) union all
(select user, null, count(*)
from purchase
group by user
)
) rp
group by user
It is possible to using union like this
SELECT SUM(`amount`-aidx) AS balance
FROM(
SELECT SUM(`amount`) as amount, 0 as aidx
from `recharge` where recharge.user = 3
union
select 0 as amount, COUNT(purchase.idx) as aidx
from `purchase`
WHERE purchase.user = 3 )a

MySQL query for distinct rows on count

I have such query that gives me results about bestseller items from shops, at the moment it works fine, but now I want to get only one product from each shop so to have a distinct si.shop_id only one bestseller product from a shop
SELECT `si`.`id`, si.shop_id,
(SELECT COUNT(*)
FROM `transaction_item` AS `tis`
JOIN `transaction` as `t`
ON `t`.`id` = `tis`.`transaction_id`
WHERE `tis`.`shop_item_id` = `si`.`id`
AND `t`.`added_date` >= '2014-02-26 00:00:00')
AS `count`
FROM `shop_item` AS `si`
INNER JOIN `transaction_item` AS `ti`
ON ti.shop_item_id = si.id
GROUP BY `si`.`id`
ORDER BY `count` DESC LIMIT 7
and that gives mu a result like:
+--------+---------+-------+
| id | shop_id | count |
+--------+---------+-------+
| 425030 | 38027 | 111 |
| 291974 | 5368 | 20 |
| 425033 | 38027 | 18 |
| 291975 | 5368 | 12 |
| 142776 | 5368 | 10 |
| 397016 | 38027 | 9 |
| 291881 | 5368 | 8 |
+--------+---------+-------+
any ideas?
EDIT
so I created a fiddle for it
http://sqlfiddle.com/#!2/cfc4c/1
Now the query returns best selling products I want it to return only one product from shopso the result of fiddle should be
+----+---------+-------+
| ID | SHOP_ID | COUNT |
+----+---------+-------+
| 1 | 222 | 3 |
| 4 | 333 | 2 |
| 8 | 555 | 1 |
| 9 | 777 | 1 |
+----+---------+-------+
Possibly something like this:-
SELECT si.shop_id,
SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(':', si.id, sub1.item_count) ORDER BY sub1.item_count DESC), ',', 1) AS `count`
FROM shop_item AS si
INNER JOIN
(
SELECT tis.shop_item_id, COUNT(*) AS item_count
FROM transaction_item AS tis
JOIN `transaction` as t
ON t.id = tis.transaction_id
AND t.added_date >= '2014-02-26 00:00:00'
GROUP BY tis.shop_item_id
) sub1
ON sub1.shop_item_id = si.id
GROUP BY si.shop_id
ORDER BY `count` DESC LIMIT 7
The sub query gets the count of items for each shop. Then the main query concatenates the item id and the item count together, group concatenates all those for a single shop together (ordered by the count descending) and then uses SUBSTRING_INDEX to grab the first one (ie, everything before the first comma).
You will have to split up the count field to get the item id and count separately (the separator is a : ).
This is taking a few guesses about what you really want, and with no table declares or data it isn't tested.
EDIT - now tested with the SQL fiddle example:-
SELECT SUBSTRING_INDEX(`count`, ':', 1) AS ID,
shop_id,
SUBSTRING_INDEX(`count`, ':', -1) AS `count`
FROM
(
SELECT si.shop_id,
SUBSTRING_INDEX(GROUP_CONCAT(CONCAT_WS(':', si.id, sub1.item_count) ORDER BY sub1.item_count DESC), ',', 1) AS `count`
FROM shop_item AS si
INNER JOIN transaction_item AS ti
ON ti.shop_item_id = si.id
INNER JOIN
(
SELECT tis.shop_item_id, COUNT(*) AS item_count
FROM transaction_item AS tis
JOIN `transaction` as t
ON t.id = tis.transaction_id
AND t.added_date >= '2014-02-26 00:00:00'
GROUP BY tis.shop_item_id
) sub1
ON sub1.shop_item_id = si.id
GROUP BY si.shop_id
) sub2
ORDER BY `count` DESC LIMIT 7;

sum two rows and order by date / total

need some help to build a query, this is my current scheme:
users:
+----+------------+
| id | username |
+----+------------+
| 1 | rob |
| 2 | john |
| 3 | jane | <--- jane never has donated
| 4 | mike |
+----+------------+
donations:
+--------------------+------------+
| uid | amount | date |
+---------+----------+------------+
| 1 | 20 | 2013-10-10 |
| 2 | 5 | 2013-10-03 |
| 2 | 50 | 2013-09-25 |
| 2 | 5 | 2013-10-01 |
| 4 | 100 | 2012-10-01 | <-- past year
+---------+----------+------------+
Result I want:
+---------+-------------+---------+-------------+---------------+----------+
| id | username | amount | monthly | totalamount | total |
+---------+-------------+---------+-------------+ --------------+----------+
| 1 | rob | 20 | 1 | 20 | 1 |
| 2 | john | 60 | 3 | 60 | 3 |
| 3 | jane | 0 | 0 | 0 | 0 |
| 4 | mike | 0 | 0 | 100 | 1 |
+---------+-------------+-----------------------+---------------+----------+
This is my query:
SELECT
u.*,
COALESCE(sum(d.amount), 0) amount,
COUNT(d.uid) monthly,
COUNT(d.amount) as Total, <-- need to get sum all time donations and number of times donated
FROM users u
LEFT JOIN donations d
ON u.id = d.uid
AND (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE()))
GROUP BY u.id ORDER BY u.id ASC
So i need to add 2 different sums from same data.
EDIT: http://sqlfiddle.com/#!2/20a974/9 schema and data
How I can do this?
For this we need to filter the data on the select and not on the join.
Remove this condition:
AND (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE()))
and add this to the select:
SUM (CASE WHEN (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE())) THEN 1 ELSE 0 END) as monthly
Edit:
whole query:
SELECT users.id, users.username,
COALESCE(sum(CASE WHEN (month(donations.date), year(donations.date)) = (month(CURDATE()), year(CURDATE())) THEN donations.amount ELSE 0 END), 0) monthly_sum,
COALESCE(sum(CASE WHEN (month(donations.date), year(donations.date)) = (month(CURDATE()), year(CURDATE())) THEN 1 ELSE 0 END), 0) monthly_amount,
COALESCE(sum(donations.amount), 0) total_sum,
count(*) total_amount
from users
left join donations
on donations.uid = users.id
group by users.id, users.username
http://sqlfiddle.com/#!2/20a974/20/0
For me the easiest way to think about the separately grouped information is to put it into separate queries and then just join the results back together. This is not likely to be the most efficient, but it helps to get something working.
select auo.id, auo.username,
coalesce(monthly_count, 0), coalesce(monthly_total, 0),
coalesce(total, 0), coalesce(total_amount, 0)
from aaa_users auo
left join (
select au.id as id, count(adm.amount) as monthly_count, SUM(adm.amount) as monthly_total
from aaa_users au join aaa_donations adm on au.id = adm.uid and adm.donate_date > GETDATE()-30
group by au.id
) as monthly on monthly.id = auo.id
left join (
select au.id as id, count(ady.amount) total, SUM(ady.amount) as total_amount
from aaa_users au join aaa_donations ady on au.id = ady.uid and ady.donate_date > getDate()-450
group by au.id
) as yearly on yearly.id = auo.id
As #CompuChip said, it's cleaner to just join to the donations table twice, but I have something wrong in my join logic as the values for john are getting duplicated. I think there would need to be a donations.id column to prevent the monthly and total donations from being combined. Anyway, here's an example even though it isn't working correctly
select au.id, au.username,
count(adm.amount), SUM(adm.amount) as monthly_total,
count(ady.amount), SUM(ady.amount) as total_amount
from aaa_users au
left outer join aaa_donations adm on au.id = adm.uid and adm.donate_date > GETDATE()-60
left outer join aaa_donations ady on au.id = ady.uid and ady.donate_date > getDate()-450
group by au.id, au.username
order by au.id, au.username
You can do another join to donations, giving it a different alias: LEFT JOIN donations d2 on d2.uid = u.id. Then sum over d2.amount for the last two fields, e.g.
SELECT u.*,
COALESCE(sum(d.amount), 0) amount,
COUNT(d.uid) monthly,
COUNT(d.amount) as Total,
COALESCE(sum(d2.amount), 0) amountAll,
COUNT(d2.uid) monthlyAll,
COUNT(d2.amount) as TotalAll
FROM users u
LEFT JOIN donations d ON u.id = d.uid AND (month(d.date), year(d.date)) = (month(CURDATE()), year(CURDATE()))
LEFT JOIN donations d2 ON u.id = d2.uid
GROUP BY u.id ORDER BY u.id ASC

Understanding NOT EXISTS

I'm trying to get the hang of NOT EXISTS and am having some trouble.
Say I have a 2 tables.
Employees:
+------+------+
| eid | name |
+------+------+
| 1 | Bob |
| 2 | Alice|
| 3 | Jill |
+------+------+
Transactions:
+----------+----------+----------+-----------+
| tid | eid | type | amount |
+----------+----------+----------+-----------+
| 1 | 1 | Deposit | 50 |
| 2 | 1 | Open | 500 |
| 3 | 3 | Open | 200 |
| 4 | 2 | Withdraw | 25 |
| 5 | 2 | Open | 100 |
+----------+----------+----------+-----------+
Let's say I want to find the names of all the employees that have not opened any account with the amount of $250 or higher. This means that I only want the rows where an employee has opened an account of amount < $250.
Right now I have something like this...
SELECT name FROM Employees e
WHERE NOT EXISTS (
SELECT * FROM Transactions t
WHERE t.type <> 'Open' AND t.amount >= 250 AND t.eid = e.eid);
This is obviously wrong and I don't really understand why.
You need to combine an EXISTS with a NOT EXISTS since you "only want the rows where an employee has opened an account of amount < $250.":
SELECT name FROM Employees e
WHERE EXISTS (
SELECT 1 FROM Transactions t
WHERE t.amount < 250 AND t.type='Open' AND t.eid = e.eid)
AND NOT EXISTS (
SELECT 1 FROM Transactions t
WHERE t.amount >= 250 AND t.eid = e.eid);
You need the EXISTS to ensure that only employee are returned which have an open account with amount < 250 at all. The NOT EXISTS is required to ensure that not employee are included which have additional accounts with amount >= 250.
Here's a sql-fiddle demo
The only issue I see - is that you've used <> for transaction type, not =
SELECT name FROM Employees e
WHERE NOT EXISTS (
SELECT null FROM Transactions t
WHERE t.transaction_type = 'Open' AND t.amount >= 250 AND t.eid = e.eid);
After you edited your question the answer would be:
SELECT name FROM Employees e
WHERE EXISTS (
SELECT null FROM Transactions t
WHERE t.transaction_type = 'Open' AND t.amount < 250 AND t.eid = e.eid);
I'd recommend using an LEFT JOIN instead of a sub select.
SELECT name FROM Employees e
LEFT JOIN Transactions t
ON e.eid = t.eid
WHERE t.tid IS NULL
OR t.type <> 'Open'
OR t.amount <= 250;
This will join all transaction records, and then only include records where a transaction does not exist, the user has a non-open transaction, or the amount doesn't meet the reuiqred $250