mySQL - subquery with multiple fields - mysql

I have 2 tables.
Table Matches
id | TeamA | TeamB
--------------
1 | Barça | Madrid
2 | Valencia | Depotivo
Table Payments
idMatch | User | Quantity
---------------------------
1 | Me | 50
2 | Me | 100
Then in one query I want to get TeamA, TeamB, User and Quantity if they have same id.
I've tried this but it fails.
SELECT TeamA, TeamB FROM Matches WHERE id IN (SELECT idMatch, TeamA, TeamB FROM Payments)

try this
SELECT TeamA, TeamB ,User , Quantity FROM Matches m
inner join Payments p
on p.idMatch = m.id
WHERE id IN
(SELECT idMatch FROM Payments)
DEMO HERE

you can use joins
select Matches.TeamA,Matches.TeamB,Payments.User,Payments.Quantity from Matches,Payments where Matches.id=Payments.idMatch

try this
SELECT payments.user,
payments.quantity
FROM
Payments
INNER JOIN Matches ON (Matches.id = Payments.idMatch);

IN() should contain a list of values like IN('red','green','blue') or IN(1,3,53). SELECT is fine, but it has to return a single field. This would work.
SELECT TeamA, TeamB FROM Matches
WHERE id IN (
SELECT idMatch FROM Payments
)
However it looks like you want to achieve something you need JOIN or GROUP BY for.

Try
SELECT
TeamA,
TeamB,
payments.user,
payments.quantity
FROM
matches
JOIN payments ON ( matches.id = payments.matchid )

select m.TeamA,m.TeamB,p.`User`,p.Quantity from matches m INNER join
payments p where p.idMatch=m.id

Related

sum of count(*) for all rows in MySQL

I'm stuck with sum() query where I want the sum of count(*) values in all rows with group by.
Here is the query:
select
u.user_type as user,
u.count,
sum(u.count)
FROM
(
select
DISTINCT
user_type,
count(*) as count
FROM
users
where
(user_type = "driver" OR user_type = "passenger")
GROUP BY
user_type
) u;
Current Output:
----------------------------------
| user | count | sum |
----------------------------------
| driver | 58 | 90 |
----------------------------------
Expected Output:
----------------------------------
| user | count | sum |
----------------------------------
| driver | 58 | 90 |
| passenger | 32 | 90 |
----------------------------------
If I remove sum(u.count) from query then output is looks like:
--------------------------
| user | count |
--------------------------
| driver | 58 |
| passenger | 32 |
--------------------------
You need a subquery:
SELECT user_type,
Count(*) AS count,
(SELECT COUNT(*)
FROM users
WHERE user_type IN ("driver","passenger" )) as sum
FROM users
WHERE user_type IN ("driver","passenger" )
GROUP BY user_type ;
Note you dont need distinct here.
OR
SELECT user_type,
Count(*) AS count,
c.sum
FROM users
CROSS JOIN (
SELECT COUNT(*) as sum
FROM users
WHERE user_type IN ("driver","passenger" )
) as c
WHERE user_type IN ("driver","passenger" )
GROUP BY user_type ;
You can use WITH ROLLUP modifier:
select coalesce(user_type, 'total') as user, count(*) as count
from users
where user_type in ('driver', 'passenger')
group by user_type with rollup
This will return the same information but in a different format:
user | count
----------|------
driver | 32
passenger | 58
total | 90
db-fiddle
In MySQL 8 you can use COUNT() as window function:
select distinct
user_type,
count(*) over (partition by user_type) as count,
count(*) over () as sum
from users
where user_type in ('driver', 'passenger');
Result:
user_type | count | sum
----------|-------|----
driver | 32 | 90
passenger | 58 | 90
db-fiddle
or use CTE (Common Table Expressions):
with cte as (
select user_type, count(*) as count
from users
where user_type in ('driver', 'passenger')
group by user_type
)
select user_type, count, (select sum(count) from cte) as sum
from cte
db-fiddle
I would be tempted to ask; Are you sure you need this at the DB level?
Unless you are working purely in the database layer, any processing of these results will be built into an application layer and will presumably require some form of looping through the results
It could be easier, simpler, and more readable to run
SELECT user_type,
COUNT(*) AS count
FROM users
WHERE user_type IN ("driver", "passenger")
GROUP BY user_type
.. and simply add up the total count in the application layer
As pointed out by Juan in another answer, the DISTINCT is redundant as the GROUP BY ensures that each resultant row is different
Like Juan, I also prefer an IN here, rather than OR condition, for the user_type as I find it more readable. It also reduces the likelihood of confusion if combining further AND conditions in the future
As an aside, I would consider moving the names of the user types, "driver" and "passenger" into a separate user_types table and referencing them by an ID column from your users table
N.B. If you absolutely do need this at the DB level, I would advocate using one of Paul's excellent options, or the CROSS JOIN approach proffered by Tom Mac, and by Juan as his second suggested solution
Try this. Inline view gets the overall total :
SELECT a.user_type,
count(*) AS count,
b.sum
FROM users a
JOIN (SELECT COUNT(*) as sum
FROM users
WHERE user_type IN ("driver","passenger" )
) b ON TRUE
WHERE a.user_type IN ("driver","passenger" )
GROUP BY a.user_type;
You could simply combine SUM() OVER() with COUNT(*):
SELECT user_type, COUNT(*) AS cnt, SUM(COUNT(*)) OVER() AS total
FROM users WHERE user_type IN ('driver', 'passenger') GROUP BY user_type;
db<>fiddle demo
Output:
+------------+------+-------+
| user_type | cnt | total |
+------------+------+-------+
| passenger | 58 | 90 |
| driver | 32 | 90 |
+------------+------+-------+
Add a group by clause at the end for user-type, e.g:
select
u.user_type as user,
u.count,
sum(u.count)
FROM
(
select
DISTINCT
user_type,
count(*) as count
FROM
users
where
(user_type = "driver" OR user_type = "passenger")
GROUP BY
user_type
) u GROUP BY u.user_type;
Tom Mac Explain Properly Your answer. Here is the another way you can do that.
I check the query performance and not found any difference within 1000 records
select user_type,Countuser,(SELECT COUNT(*)
FROM users
WHERE user_type IN ('driver','passenger ') )as sum from (
select user_type,count(*) as Countuser from users a
where a.user_type='driver'
group by a.user_type
union
select user_type,count(*) as Countuser from users b
where b.user_type='passenger'
group by b.user_type
)c
group by user_type,Countuser
Try this:
WITH SUB_Q AS (
SELECT USER_TYPE, COUNT (*) AS CNT
FROM USERS
WHERE USER_TYPE = "passenger" OR USER_TYPE = "driver"
GROUP BY USER_TYPE
),
SUB_Q2 AS (
SELECT SUM(CNT) AS SUM_OF_COUNT
FROM SUB_Q
)
SELECT A.USER_TYPE, A.CNT AS COUNT, SUB_Q2 AS SUM
FROM SUB_Q JOIN SUB_Q2 ON (TRUE);
I used postgresql dialect but you can easily change to a subquery.
select
u.user_type as user,
u.count,
sum(u.count)
FROM users group by user

Query: I have 4 rows, need to add the results from 3 rows into one, and leave the last row untouched

I have a kind of tricky question for this query. First the code:
SELECT user_type.user_type_description,COUNT(incident.user_id) as Quantity
FROM incident
INNER JOIN user ON incident.user_id=user.user_id
INNER JOIN user_type ON user.user_type=user_type.user_type
WHERE incident.code=2
GROUP BY user.user_type
What Am I doing?
For example, I am counting police reports of robbery, made from different kind of users. In my example, "admin" users reported 6 incidents of code "2" (robbery) and so on, as is showed in 'where' clause (incident must be robbery, also code 2).
this brings the following result:
+-----------------------+----------+
| user_type_description | Quantity |
+-----------------------+----------+
| Admin | 6 |
| Moderator | 8 |
| Fully_registered_user | 8 |
| anonymous_user | 9 |
+-----------------------+----------+
Basically Admin,Moderator and Fully_registered_user are appropriately registered users. I need to add them in a result where it shows like:
+--------------+------------+
| Proper_users | Anonymous |
+--------------+------------+
| 22 | 9 |
+--------------+------------+
I am not good with sql. Any help is appreciated. Thanks.
You can try to use condition aggregate function base on your current result set.
SUM with CASE WHEN expression.
SELECT SUM(CASE WHEN user_type_description IN ('Admin','Moderator','Fully_registered_user') THEN Quantity END) Proper_users,
SUM(CASE WHEN user_type_description = 'anonymous_user' THEN Quantity END) Anonymous
FROM (
SELECT user_type.user_type_description,COUNT(incident.user_id) as Quantity
FROM incident
INNER JOIN user ON incident.user_id=user.user_id
INNER JOIN user_type ON user.user_type=user_type.user_type
WHERE incident.code=2
GROUP BY user.user_type
) t1
You just need conditional aggregation:
SELECT SUM( ut.user_type_description IN ('Admin', 'Moderator', 'Fully_registered_user') ) as Proper_users,
SUM( ut.user_type_description IN ('anonymous_user') as anonymous
FROM incident i INNER JOIN
user u
ON i.user_id = u.user_id INNER JOIN
user_type ut
ON u.user_type = ut.user_type
WHERE i.code = 2;
Notes:
Table aliases make the query easier to write and to read.
This uses a MySQL shortcut for adding values -- just just adding the booelean expressions.
I would solve it with a CTE, but it would be better to have this association in a table.
WITH
user_type_categories
AS
(
SELECT 'Admin' AS [user_type_description] , 'Proper_users' AS [user_type_category]
UNION SELECT 'Moderator' AS [user_type_description] , 'Proper_users' AS [user_type_category]
UNION SELECT 'Fully_registered_user' AS [user_type_description] , 'Proper_users' AS [user_type_category]
UNION SELECT 'anonymous_user' AS [user_type_description] , 'Anonymous' AS [user_type_category]
)
SELECT
CASE WHEN utc.[user_type_category] = 'Proper_users' THEN
SUM(incident.user_id)
END AS [Proper_Users_Quantity]
, CASE WHEN utc.[user_type_category] = 'Anonymous' THEN
SUM(incident.user_id)
END AS [Anonymous_Quantity]
FROM
[incident]
INNER JOIN [user] ON [incident].[user_id] = [user].[user_id]
INNER JOIN [user_type] ON [user].[user_type] = [user_type].[user_type]
LEFT JOIN user_type_categories AS utc ON utc.[user_type_description] = [user_type].[user_type_description]
WHERE
[incident].[code] = 2

SQL find team that only contains specified 2 users

I have a table called team_members with this structure and contents:
+---------+---------+
| team_id | user_id |
+---------+---------+
| 1 | 18 |
+---------+---------+
| 1 | 7 |
+---------+---------+
| 3 | 18 |
+---------+---------+
What i am trying to do is to find a team that only contains 2 users and this users are supplied by me (in this case users with id 7 and 18). Unfortunately, i am having no ideas about how to make this query properly. I have tried something like
SELECT a.team_uid
FROM team_members a
INNER JOIN (
SELECT team_uid, user_id, COUNT(*) cnt_team
FROM team_members
GROUP BY team_uid
HAVING COUNT(*) = 2
) b ON a.user_id = b.user_id
Use Case statement in Having clause and Count only the required user_id's. Try this.
select teamid from yourtable
group by teamid
having count(case when userid=7 then 1 end)=1
and count(case when userid=18 then 1 end)=1
and count(1)=2
Something to think about (and assuming a PK on team_id,user_id)...
SELECT x.*, COUNT(*),SUM(user_id IN(7,18)) FROM my_table x GROUP BY team_id;
A couple more ways to do this (where $id1 and $id2 are the users in question):
SELECT team_id
FROM team_members
GROUP BY team_id
HAVING COUNT(*) = 2
AND MIN(user_id) = LEAST($id1,$id2)
AND MAX(user_id) = GREATEST($id1,$id2)
See SQL Fiddle Demo here with values of 7 and 18 for $id1 and $id2. I am using LEAST() and GREATEST() in case it's not known which is the higher and which is the lower (for example, if they're coming from user input).
SELECT team_id
FROM team_members
GROUP BY team_id
HAVING GROUP_CONCAT(user_id ORDER BY user_id) = ('7,18')
See SQL Fiddle Demo here. Again, if it isn't known which is the higher and which is the lower, then this might be written as (the ORDER BY in GROUP_CONCAT() would be unnecessary):
SELECT team_id
FROM team_members
GROUP BY team_id
HAVING GROUP_CONCAT(user_id) IN ('$id1,$id2','$id2,$id1')
You can also use:
select team_id
from team_members
group by team_id
having sum(user_id not in(7, 18)) = 0
Example above assumes you want teams with only users 7 or 18 (no others, but not necessarily both).
If you want teams with BOTH users 7 and 18, and no others, you can use:
select team_id
from team_members
group by team_id
having sum(user_id not in(7, 18)) = 0
and sum(user_id in(7, 18)) = 2

Select from one table but filtering other two

Let's say i've got this database:
book
| idBook | name |
|--------|----------|
| 1 |Book#1 |
category
| idCateg| category |
|--------|----------|
| 1 |Adventures|
| 2 |Science F.|
book_categ
| id | idBook | idCateg | DATA |
|--------|--------|----------|--------|
| 1 | 1 | 1 | (null) |
| 2 | 1 | 2 | (null) |
I'm trying to select only the books which are in category 1 AND category 2 something like this
SELECT book.* FROM book,book_categ
WHERE book_categ.idCateg = 1 AND book_categ.idCateg = 2
Obviously, this giving 0 results becouse each row has only one idCateg it does work width OR but the results are not what I need. I've also tried to use a join, but I just can't get the results I expect.
Here it's the SQLFiddle of my current project, with my current DB, the data at the begining is just a sample. SQLFiddle
Any help will be really appreciated.
Solution using EXISTS:
select *
from book b
where exists (select 'x'
from book_categ x
where x.idbook = b.idbook
and x.idcateg = 1)
and exists (select 'x'
from book_categ x
where x.idbook = b.idbook
and x.idcateg = 2)
Solution using join with an inline view:
select *
from book b
join (select idbook
from book_categ
where idcateg in (1, 2)
group by idbook
having count(*) = 2) x
on b.idbook = x.idbook
You could try using ALL instead of IN (if you only want values that match all criteria to be returned):
SELECT book.*
FROM book, book_categ
WHERE book_categ.idCateg = ALL(1 , 2)
One way to get the result is to do join to the book_categ table twice, something like
SELECT b.*
FROM book b
JOIN book_categ c1
ON c1.book_id = b.id
AND c1.idCateg = 1
JOIN book_categ c2
ON c2.book_id = b.id
AND c2.idCateg = 2
This assumes that (book_id, idCateg) is constrained to be unique in the book_categ table. If it isn't unique, then this query can return duplicate rows. Adding a GROUP BY clause or the DISTINCT keyword will eliminate any generated duplicates.
There are several other queries that can get generate the same result.
For example, another approach to finding book_id that are in two categories is to get all the rows with idCateg values of 1 or 2, and then GROUP BY book_id and get a count of DISTINCT values...
SELECT b.*
FROM book b
JOIN ( SELECT d.book_id
FROM book_categ d
WHERE d.idCateg IN (1,2)
GROUP BY d.book_id
HAVING COUNT(DISTINCT d.idCateg) = 2
) c
ON c.book_id = b.id

Inner join with where

I need to get Max bid and that username but result is coming in wrong way
Here is my two tables
Product
id | name | username
1 | A | deen
2 B | ann
Bid
id | c_bid | username
1 | 10 | ann
1 | 12 | ann
1 | 13 | ann
2 | 10 | ann
1 | 15 | Hel
1 | 16 | Hel
SQL
SELECT name, bid.username AS username, MAX(bid.c_bid) AS c_bid FROM product INNER JOIN bid
ON gems.id= bidding.id WHERE bid.id = '1'
Result
name | c_bid | username
A | 16 | ann
Why is this result coming on this sql?
Since you are using an aggregate function you need to include a GROUP BY.
SELECT name,
bid.username AS username,
MAX(bid.c_bid) AS c_bid
FROM product
INNER JOIN bid
ON product.id= bid.id
WHERE bid.id = '1'
GROUP BY name, bid.username
See SQL Fiddle with Demo
You are getting strange results because MySQL uses an Extension to GROUP BY that allows you to use an aggregate function without using a GROUP BY but this could cause your result to be incorrect.
The GROUP BY makes it so you will return the max(bid) for each item that you are grouping by, in your case it is name and `username.
If you want to return only one max(bid) for each product id, then you could use:
SELECT name,
bid.username AS username,
bid.c_bid
FROM product
INNER JOIN bid
ON product.id= bid.id
INNER JOIN
(
SELECT max(c_bid) c_bid, id
FROM bid
GROUP BY id
) b
on bid.id = b.id
and bid.c_bid = b.c_bid
WHERE bid.id = '1'
See SQL Fiddle with Demo
Use GROUP BY.
SELECT name, bid.username AS username, MAX(bid.c_bid) AS c_bid FROM product INNER JOIN bid
ON gems.id= bidding.id WHERE bid.id = '1' GROUP BY bid.username;
SELECT
p.username,
MAX(b.c_bid) AS max_c_bid
FROM product p
LEFT JOIN bid b ON (p.username = b.username)
GROUP BY p.username;
SELECT
pr.name,
mb.c_bid,
pr.username
FROM
(
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY c_bid DESC), ',', 1) AS id,
MAX(c_bid) AS c_bid
FROM bid
GROUP BY id
) AS mb
JOIN product AS pr ON(pr.id = mb.id)
Example: http://sqlfiddle.com/#!2/0a6c9/2