MySQL - Group by multiple columns from same table - php

I have a table similar to the following
sno | booking_id | room_type | gender | age | amount | days
1 | 2016JUL001 | AC | Male | 25 | 1000 | 15
2 | 2016JUL001 | AC | Male | 42 | 1000 | 15
3 | 2016JUL001 | AC | Male | 28 | 1000 | 15
4 | 2016JUL010 | N AC | Female | 45 | 1000 | 15
5 | 2016JUL010 | N AC | Female | 46 | 1000 | 15
6 | 2016JUL005 | N AC | Male | 28 | 1000 | 15
7 | 2016JUL005 | N AC | Female | 35 | 1000 | 15
8 | 2016JUL009 | AC | Female | 26 | 1000 | 15
9 | 2016JUL009 | AC | Female | 25 | 1000 | 15
... so on
Expected output [If I want to get gender='Female']
sno | booking_id | room_type | gender | age | amount | days
4 | 2016JUL010 | N AC | Female | 45 | 1000 | 15
5 | 2016JUL010 | N AC | Female | 46 | 1000 | 15
8 | 2016JUL009 | AC | Female | 26 | 1000 | 15
9 | 2016JUL009 | AC | Female | 25 | 1000 | 15
Expected output [If I want to get gender='Male']
sno | booking_id | room_type | gender | age | amount | days
1 | 2016JUL001 | AC | Male | 25 | 1000 | 15
2 | 2016JUL001 | AC | Male | 42 | 1000 | 15
3 | 2016JUL001 | AC | Male | 28 | 1000 | 15
Expected output [If I want to get gender='Male' AND gender='Female']
sno | booking_id | room_type | gender | age | amount | days
6 | 2016JUL005 | N AC | Male | 28 | 1000 | 15
7 | 2016JUL005 | N AC | Female | 35 | 1000 | 15
NOTE: I want 3 separate individual QUERIES to get the above outputs
Thanks in advance

First query :
SELECT sno, booking_id, room_type, gender, age
FROM customer_data
WHERE booking_id IN ( SELECT booking_id FROM customer_data
WHERE gender='female' AND age>0 and RIGHT(booking_id,1) <> '1'
GROUP BY booking_id HAVING COUNT(*) > 1 )
ORDER BY booking_id ASC, age ASC
Second :
SELECT sno, booking_id, room_type, gender, age
FROM customer_data
WHERE booking_id IN ( SELECT booking_id FROM customer_data
WHERE gender='male' AND age>0
GROUP BY booking_id HAVING COUNT(*) > 1 )
ORDER BY booking_id ASC, age ASC
And third:
SELECT sno, booking_id, room_type, gender, age
FROM customer_data
WHERE booking_id IN ( SELECT booking_id FROM customer_data
WHERE gender IN('male','female') AND age>0
GROUP BY booking_id HAVING COUNT(distinct gender) = 2 )
ORDER BY booking_id ASC, age ASC
If in the first two you wanted only booking_id that has only 1 gender, add to the having clause :
AND COUNT(distinct gender) = 1

After lot of tries, I am able to get the data I want
Query ['Female']
SELECT sno, bd.booking_id, bd.room_type, bd.gender, bd.age
FROM customer_data bd
INNER JOIN (
SELECT booking_id, GROUP_CONCAT(DISTINCT gender) AS g
FROM customer_data
WHERE gender!='' AND age>0
GROUP BY booking_id
HAVING COUNT(booking_id) > 1
ORDER BY booking_id ASC, gender DESC
) cbd
WHERE cbd.booking_id = bd.booking_id AND cbd.g = 'Female'
Query ['Male']
SELECT sno, bd.booking_id, bd.room_type, bd.gender, bd.age
FROM customer_data bd
INNER JOIN (
SELECT booking_id, GROUP_CONCAT(DISTINCT gender) AS g
FROM customer_data
WHERE gender!='' AND age>0
GROUP BY booking_id
HAVING COUNT(booking_id) > 1
ORDER BY booking_id ASC, gender DESC
) cbd
WHERE cbd.booking_id = bd.booking_id AND cbd.g ='Male'
Query ['Male and Female']
SELECT sno, bd.booking_id, bd.room_type, bd.gender, bd.age
FROM customer_data bd
INNER JOIN (
SELECT booking_id, GROUP_CONCAT(DISTINCT gender ORDER BY gender DESC) AS g
FROM customer_data
WHERE gender!='' AND age>0
GROUP BY booking_id
HAVING COUNT(booking_id) > 1
ORDER BY booking_id ASC, gender DESC
) cbd
WHERE cbd.booking_id = bd.booking_id AND cbd.g = 'Male,Female'

Your schema appears somewhat flawed. Nevertheless, here's something to think about...
SELECT booking_id
, COUNT(DISTINCT gender) x
FROM customer_data
WHERE gender IN ('Male','Female') <-- not strictly necessary if there are only two genders.
GROUP
BY booking_id;

Related

Stuck in building MySQL query

Given an example of table:
id | item_id | user_id | bid_price
----------------------------------
The task is to select rows with minimum bid_price for each item_id in the provided set.
For example: item_id = [1, 2, 3] - so I need to select up to three (3) rows, having a minimum bid_price.
Example of data:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
2 | 1 | 12 | 2
3 | 1 | 13 | 3
4 | 1 | 14 | 1
5 | 1 | 15 | 4
6 | 2 | 16 | 2
7 | 2 | 17 | 1
8 | 3 | 18 | 2
9 | 3 | 19 | 3
10 | 3 | 18 | 2
Expected result:
id | item_id | user_id | bid_price
----------------------------------
1 | 1 | 11 | 1
7 | 2 | 17 | 1
8 | 3 | 18 | 2
Actually, I'm using Symfony/Docine DQL, but it will be enough with a plain SQL example.
For the all the columns in the rows you could use a inner join on subselect for min bid price
select m.id, m.item_id, m.user_id, m.bid_price
from my_table m
inner join (
select item_id, min(id) min_id, min(bid_price) min_price
from my_table
where item_id IN (1,2,3)
group by item_id
) t on t.item_id = m.item_id
and t.min_price= m.bid_price
and t.min_id = m.id
or .. if you have some float data type you could use a acst for unsigned
select m.id, m.item_id, m.user_id, cast(m.bid_price as UNSIGNED)
from my_table m
inner join (
select item_id, min(id) min_id, min(bid_price) min_price
from my_table
where item_id IN (1,2,3)
group by item_id
) t on t.item_id = m.item_id
and t.min_price= m.bid_price
and t.min_id = m.id
You can use MIN() with GROUP BY in the query:
SELECT id, item_id, MIN(bid_price) AS min_bid, user_id
FROM your_tbl
GROUP BY item_id
HAVING item_id in(1, 2, 3);
Use this query:
SELECT id, item_id, user_id, min(bid_price) as bid_price
FROM YOUR_TABLE_NAME
GROUP BY item_id;

How do I pivot MySQL INNER JOIN results in PHP?

I have the following 2 MySQL tables:
players:
| id | name |
|----|---------|
| 1 | Player1 |
| 2 | Player2 |
| 3 | Player3 |
scores:
| key | id | round | score |
|-----|----|-------|-------|
| 1 | 1 | Rd1 | 20 |
| 2 | 1 | Rd2 | 22 |
| 3 | 1 | Rd3 | 19 |
| 4 | 2 | Rd1 | 18 |
| 5 | 2 | Rd2 | 23 |
| 6 | 2 | Rd3 | 19 |
where scores.id=players.id
I will have upwards of 90 players in my 'players' table, what's the best way to query this and insert it into an HTML table to make it easier to view? I'm hoping to have an output similar to this:
| Player | Round 1 | Round 2 | Round 3 |
|---------|---------|---------|---------|
| Player1 | 20 | 22 | 19 |
| Player2 | 18 | 23 | 19 |
This is my first attempt at normalizing data in tables. Am I going to have to do number of cases? I'm not sure what the best way to pivot the data is with an INNER JOIN.
This is my solution, hope it helps :
SELECT
name as Player,
SUM(CASE WHEN (s.round='Rd1') THEN s.score ELSE 0 END) AS Round1,
SUM(CASE WHEN (s.round='Rd2') THEN s.score ELSE 0 END) AS Round2,
SUM(CASE WHEN (s.round='Rd3') THEN s.score ELSE 0 END) AS Round3
FROM
players p
JOIN scores s
on s.id=p.id
GROUP BY
name
This will output :
| Player | Round1 | Round2 | Round3 |
|---------|---------|---------|---------|
| Player1 | 20 | 22 | 19 |
| Player2 | 18 | 23 | 19 |
This Fiddle for you to test!
I have a sligthly alternative solution which uses subqueries with the following benefit that players with no no score gets listed too!!
SELECT
p.name,
ifnull((select score from scores where id = p.id and round='Rd1' limit 1), 0) as Round1,
ifnull((select score from scores where id = p.id and round='Rd2' limit 1), 0) as Round2,
ifnull((select score from scores where id = p.id and round='Rd3' limit 1), 0) as Round3
FROM players p
GROUP BY p.name, p.id

Select distinct rows order by highest values

I have a table like
Name | Image | Points | Country
-------------------------------
Bob | a.jpg | 100 | USA
Bob | b.jpg | 56 | USA
Sal | c.jpg | 87 | UK
Jim | d.jpg | 34 | UK
Bet | e.jpg | 23 | USA
Bren | f.jpg | 5 | USA
Bren | g.jpg | 15 | UK
Test | h.jpg | 10 | USA
I want to get the top 4 highest rows based on the "Points" column where the country is "USA" and removing duplicate "Names", so the desired outcome would be
Name | Image | Points | Country
-------------------------------
Bob | a.jpg | 100 | USA
Bet | e.jpg | 23 | USA
Test | h.jpg | 10 | USA
Bren | f.jpg | 5 | USA
Any help would be appreciated thank you
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(image VARCHAR(12) NOT NULL PRIMARY KEY
,name VARCHAR(12) NOT NULL
,points INT NOT NULL
,country VARCHAR(12) NOT NULL
);
INSERT INTO my_table VALUES
('a.jpg','Bob' ,100,'USA'),
('b.jpg','Bob' , 56,'USA'),
('c.jpg','Sal' , 87,'UK'),
('d.jpg','Jim' , 34,'UK'),
('e.jpg','Bet' , 23,'USA'),
('f.jpg','Bren', 5,'USA'),
('g.jpg','Bren', 15,'UK'),
('h.jpg','Test', 10,'USA');
SELECT a.*
FROM my_table a
JOIN
( SELECT name,MAX(points) points FROM my_table WHERE country = 'USA' GROUP BY name ) b
ON b.name = a.name
AND b.points = a.points
ORDER
BY points DESC
LIMIT 4;
+-------+------+--------+---------+
| image | name | points | country |
+-------+------+--------+---------+
| a.jpg | Bob | 100 | USA |
| e.jpg | Bet | 23 | USA |
| h.jpg | Test | 10 | USA |
| f.jpg | Bren | 5 | USA |
+-------+------+--------+---------+
select table.* from table join
(select Name, max(Points) mp from table where Country='USA' group by Name
order by mp desc limit 4) t
on table.Name = t.Name and table.Points = t.mp and table.Country='USA'
order by table.points desc
When a person's max point row have two record, eg bob's b.jpg is also 100, this would lead to multi rows in bobs result.
Try This:
SELECT Name,Image,MAX(points),country
FROM table_1
ORDER BY points desc
GROUP BY Name,points
LIMIT 4

MySQL count column from multiple tables. Group results by primary id

I have the following 3 MySQL tables:
products
| id | name | comment_count |
|----|-----------|---------------|
| 1 | Product A | 10 |
| 2 | Product B | 20 |
| 3 | Product C | 30 |
products_views
| product_id | pv_count | pv_date |
| -----------|-------|------------|
| 1 | 10 | 2015-01-01 |
| 1 | 10 | 2015-01-02 |
| 1 | 10 | 2015-01-03 |
| 2 | 20 | 2015-01-01 |
| 2 | 20 | 2015-01-02 |
| 2 | 20 | 2015-01-03 |
| 3 | 30 | 2015-01-01 |
| 3 | 30 | 2015-01-02 |
| 3 | 30 | 2015-01-03 |
products_likes
| product_id | pl_count | pl_date |
| -----------|-------|------------|
| 1 | 10 | 2015-01-01 |
| 1 | 10 | 2015-01-02 |
| 1 | 10 | 2015-01-03 |
| 2 | 20 | 2015-01-01 |
| 2 | 20 | 2015-01-02 |
| 2 | 20 | 2015-01-03 |
| 3 | 30 | 2015-01-01 |
| 3 | 30 | 2015-01-02 |
| 3 | 30 | 2015-01-03 |
I want to add together products.comment_count + product_views.count + product_likes.count grouping by products.id, where product_views.pv_date and product_likes.pl_date between 2015-01-01 and 2015-01-03. Ordering by a total.
What I want:
| product_id | total |
| -----------|-------|
| 3 | 210 |
| 2 | 140 |
| 1 | 70 |
Try this query
SELECT
p.product_id,
(p.comment_count + SUM(pv.count) + SUM(pl.count)) AS total
FROM products p
JOIN products_views pv ON( p.product_id = pv.product_id)
JOIN products_likes pl ON( p.product_id = pl.product_id)
WHERE pl.date BETWEEN '2015-01-01' AND '2015-01-03'
AND pv.date BETWEEN '2015-01-01' AND '2015-01-03'
GROUP BY p.product_id
ORDER BY total DESC
SELECT
(products.comment_count+products_views.pv_count+products_likes.pl_count) AS product_count
FROM products
INNER JOIN products_views.product_id=products.id
INNER JOIN products_likes.product_id=products.id
WHERE (product_views.pv_date BETWEEN dateVal AND dateVal)
AND (products_likes.pl_date BETWEEN dateVal AND dateVal)
GROUP BY products.id
ORDER BY product_count
Perform the counts "per table" before joining otherwise you run the risk of getting larger results than there should be as joins can multiply the number of rows. Also, as there is a chance some products don't exist in likes or views so a left outer join to both of the "derived tables" is recommended.
SELECT
p.product_id
, (IFNULL(p.comment_count,0) + IFNULL(pv.view_count, 0) + IFNULL(pl.like_count, 0)) AS total
FROM products p
LEFT OUTER JOIN (
SELECT
product_id
, SUM(`count`) AS view_count
FROM products_views
WHERE `date` BETWEEN '2015-01-01' AND '2015-01-03'
GROUP BY
product_id
) pv ON p.product_id = pv.product_id
LEFT OUTER JOIN (
SELECT
product_id
, SUM(`count`) AS like_count
FROM products_likes
WHERE `date` BETWEEN '2015-01-01' AND '2015-01-03'
GROUP BY
product_id
) pl ON p.product_id = pl.product_id
GROUP BY
p.product_id
ORDER BY
total DESC
btw: date and count are reserved words in most SQL dialects; it not a good idea to name columns as date or count; use something more e.g. date_entered, date_created, view_count, like_count etc.
select p.product_id, sum(p.comment_count) + sum(v.count) + sum(l.count) as total from products p join products_views v on p.product_id = v.product_id join products_likes l on p.product_id = l.product_id group by p.product_id
where l.date between '2015-01-01' and '2015-01-03'
order by total desc
try this

Get customers that has more than 2 orders but logged in less than 3 times though SQL

I'm trying to get all customers who has more than 2 orders, but only logged in less than 3 times.
I'm logging when users are logging in.
But for some reason my returns only one row with wrong data...
"user_log" table (user_id 19 has logged in only once)
| user_log_id | date | user_id | type | module_id | unit_id |
|-------------|------|---------|------|-----------|---------|
| 1 |"date"| 19 | 1 | NULL | NULL |
| 2 |"date"| 20 | 1 | NULL | NULL |
| 3 |"date"| 20 | 1 | NULL | NULL |
| 4 |"date"| 20 | 1 | NULL | NULL |
| 5 |"date"| 20 | 1 | NULL | NULL |
|-------------|------|---------|------|-----------|---------|
"orders" table where user_id 19 has 2 orders (Removed unnecessary columns)
| order_id | user_id | status |
|----------|---------|--------|
| 10 | 19 | 1 |
| 11 | 19 | 1 |
| 12 | 20 | 1 |
| 13 | 21 | 1 |
| 14 | 31 | 1 |
|----------|---------|--------|
What i want (User_id has 2 orders, but has logged in less than 3 times)
| user_id |
|---------|
| 19 |
|---------|
This is how my SQL looks like right now.
$sql = "SELECT
ul.*, orders.order_id, orders.user_id, orders.firstname, orders.lastname, COUNT(ul.user_id) AS occourcence
FROM
orders
LEFT JOIN
user_log AS ul
ON
orders.user_id = ul.user_id
WHERE
orders.status = 1
AND
ul.type = 1
GROUP BY
orders.user_id
HAVING
COUNT(orders.user_id) > 1
ORDER BY
orders.order_id DESC";
select user_id,count(user_log_id) from user_log
where user_id in
(
select user_id from Orderes
group by user_id
having count(order_id) =2
)
group by user_id
having count(user_log_id) < 3
Avoiding any sub queries (and assuming you meant logged in 3 times, and making 2 or more orders - to match your example data):-
SELECT a.user_id
FROM user_log a
INNER JOIN orders b
ON a.user_id = b.user_id
GROUP BY a.user_id
HAVING COUNT(DISTINCT user_log_id) < 3 AND COUNT(DISTINCT order_id) >= 2;
SQL fiddle here:-
http://www.sqlfiddle.com/#!2/1b719/3
Try this
select o.user_id from
(
select user_id from orders
where status=1
group by user_id having count(*)>=2
) as o left join
(
select user_id from user_logs
where type=1
group by user_id having count(*)<3
) as l
on o.user_id=l.user_id

Categories