How to double join the sql query? - php

I have build this sql query and it works fine on phpmyadmin and as well as codeigniter;
SELECT teams.team_name,
COUNT(matches.winner) AS win
FROM matches
RIGHT JOIN teams ON teams.team_name = matches.winner
GROUP BY teams.team_name, matches.winner
ORDER BY win DESC
OUTPUT:
| team_name | win |
------------------------------
| India | 1 |
| Australia | 4 |
| England | 1 |
| South Africa | 0 |
But i need the below output: (There is another column "loser") Hence i need to apply the same query for the "loser" column similar to that i have done with "win" column.
Expected Output :
| team_name | win | loss
------------------------------
| India | 1 | 2
| Australia | 4 | 3
| England | 1 | 1
| South Africa | 0 | 2

One method would be correlated subqueries:
SELECT t.team_name,
(SELECT COUNT(*)
FROM matches m
WHERE t.team_name = m.winner
) as num_wins,
(SELECT COUNT(*)
FROM matches m
WHERE t.team_name = m.loser
) as num_loses
FROM matches t
ORDER BY num_wins DESC;

Related

How to get data for two columns using right join with MYSQL case

I want to get the categories pricing, Say if there is data in customer_category_pricing table then fetch that pricing for that specific customer. Otherwise fetch default prices from categories table.
i have tried achieving this desired result using mysql case it is working fine, but the problem is, it is returning two rows
hourly_amount_final column returns updated price then per_day_amount_final returns default price
Then in next row, hourly_amount_final column return default price and then per_day_amount_final returns updated price.
table: pr_categories
---------------------------------------------------------------------------------
| id | title | hourly_amount | per_day_amount | created_at| updated_at
---------------------------------------------------------------------------------
| 1 | Power Generation | 100.00 | 200.00 |
| 2 | Local Government | 300.00 | 400.00 |
----------------------------------------------------------
table: pr_customer_categories_pricing
------------------------------------------------------------------------------------
| id | customer_id | category_id | billing_type_id | amount | created_at | updated_at
------------------------------------------------------------------------------------
| 1 | 1 | 1 | 1 | 109 |
| 2 | 1 | 1 | 2 | 600 |
----------------------------------------------------------
table: pr_billing_types
----------------
| id | title |
--------------
| 1 | Hourly |
| 2 | Per Day |
----------------
This is the query i am working with at the moment:
SELECT c.id,c.title,
CASE
WHEN (c.hourly_amount <> (SELECT ccp.amount WHERE ccp.billing_type_id = 1))
THEN (SELECT ccp.amount WHERE ccp.billing_type_id = 1)
ELSE c.hourly_amount
END
AS hourly_amount_final,
CASE
WHEN (c.per_day_amount <> (SELECT ccp.amount WHERE ccp.billing_type_id = 2))
THEN (SELECT ccp.amount WHERE ccp.billing_type_id = 2)
ELSE c.per_day_amount
END
AS per_day_amount_final
FROM pr_customer_category_pricing AS ccp
RIGHT JOIN pr_categories AS c
ON c.id = ccp.category_id AND ccp.customer_id = 1
Expected result when there is no data in pr_customer_category_pricing
----------------------------------------------------------------------
| id | title | hourly_amount_final | per_day_amount_final |
----------------------------------------------------------------------
| 1 | Power Generation | 100.00 | 200.00 |
| 2 | Local Government | 300.00 | 600.00 |
----------------------------------------------------------------------
Expected result when there is data in pr_customer_category_pricing
----------------------------------------------------------------------
| id | title | hourly_amount_final | per_day_amount_final |
----------------------------------------------------------------------
| 1 | Power Generation | 109.00 | 600.00 |
| 2 | Local Government | 300.00 | 400.00 |
----------------------------------------------------------------------
Actual result what i'm getting:
----------------------------------------------------------------------
| id | title | hourly_amount_final | per_day_amount_final |
----------------------------------------------------------------------
| 1 | Power Generation | 109.00 | 200.00 |
| 1 | Power Generation | 100.00 | 600.00 |
| 2 | Local Government | 300.00 | 400.00 |
----------------------------------------------------------------------
What am i doing wrong? Any suggestions! Help a brother out. :S
Since there can only be one entry in the pr_customer_category_pricing table per billing type, you can simplify things by creating a derived pivot table from the pr_customer_category_pricing with values for each of the billing_type_id in separate columns. You can then simply COALESCE the value from the derived table with the value from the pr_categories for each billing_type_id:
SELECT c.id,c.title,
COALESCE(ccp.hourly_amount, c.hourly_amount) AS hourly_amount_final,
COALESCE(ccp.per_day_amount, c.per_day_amount) AS per_day_amount_final
FROM (SELECT
customer_id,
category_id,
MAX(CASE WHEN billing_type_id = 1 THEN amount END) AS hourly_amount,
MAX(CASE WHEN billing_type_id = 2 THEN amount END) AS per_day_amount
FROM pr_customer_category_pricing
GROUP BY customer_id, category_id) AS ccp
RIGHT JOIN pr_categories AS c
ON c.id = ccp.category_id AND ccp.customer_id = 1
Output:
id title hourly_amount_final per_day_amount_final
1 Power Generation 109 600
2 Local Government 300 400
Demo on dbfiddle
You can use max() aggregation with group by
SELECT c.id,c.title,
max(CASE
WHEN (c.hourly_amount <> (SELECT ccp.amount WHERE ccp.billing_type_id = 1))
THEN (SELECT ccp.amount WHERE ccp.billing_type_id = 1)
ELSE c.hourly_amount
END)
AS hourly_amount_final,
max(CASE
WHEN (c.per_day_amount <> (SELECT ccp.amount WHERE ccp.billing_type_id = 2))
THEN (SELECT ccp.amount WHERE ccp.billing_type_id = 2)
ELSE c.per_day_amount
END)
AS per_day_amount_final
FROM pr_customer_category_pricing AS ccp
RIGHT JOIN pr_categories AS c
ON c.id = ccp.category_id AND ccp.customer_id = 1
group by c.id,c.title

I want to display a summary like this based on the three tables

My 3 MYSQL tables are as follows:
Table 1: citizen
=============================
ID | Name | Sex | Address |
=============================
5 | James | Male | India
6 | Shella|Female | India
7 | Jan | Male | NY
8 | May | Female | USA
==============================
Table 2: benefits
==========================
ID| benefits
==========================
1 | SSS
2 | Coco Life
3 | PhiHealth
4 | Sunlife
==========================
Table 3: pensioners
============================
ID| benefits_ID | citizen_ID
============================
1 | 1 | 5
2 | 2 | 6
3 | 1 | 7
4 | 4 | 7
==========================
I want to display that looks like this:
====================================================================
Address | Total Citizen | Male | Female | SSS | Coco Life | Others |
====================================================================
India | 2 | 1 | 1 | 1 | 1 | 0 |
NY | 1 | 1 | 0 | 1 | 0 | 1 |
USA | 1 | 0 | 1 | 0 | 0 | 0 |
==================================================================
Anybody can give me a hint on how to do this?
You can do a Left Join from the Address table to the benefits table, via pensioners table, using the appropriate relationships. Left join will allows us to consider a Address even when there is no corresponding benefits entry for any of its citizens.
In order to count total citizens, male count and female count, you now need to use COUNT(DISTINCT ID) after the join. As Joining may create duplicate rows, as a citizen may have more than one benefits.
Also, in order to count "Other" benefits, we need to ensure that the benefit IS NOT NULL and it is NOT IN ('SSS', 'Coco Life').
In multi-table queries, it is advisable to use Aliasing for Code clarity (readability) and avoiding ambiguous behaviour.
SELECT
c.Address,
COUNT(DISTINCT CASE WHEN c.Sex = 'Male' THEN c.ID END) AS male_cnt,
COUNT(DISTINCT CASE WHEN c.Sex = 'Female' THEN c.ID END) AS female_cnt,
COUNT(DISTINCT c.ID) AS total_citizen_cnt,
COUNT(CASE WHEN b.benefits = 'SSS' THEN 1 END) AS SSS_cnt,
COUNT(CASE WHEN b.benefits = 'Coco Life' THEN 1 END) AS Coco_Life_cnt,
COUNT(CASE WHEN b.benefits IS NOT NULL AND
b.benefits NOT IN ('SSS', 'Coco Life') THEN 1 END) AS Others_cnt
FROM citizen AS c
LEFT JOIN pensioners AS p
ON p.citizen_ID = c.ID
LEFT JOIN benefits AS b
ON b.ID = p.benefits_ID
GROUP BY c.Address

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

Sql sum() columns from different tables

Category Table
mysql> SELECT * FROM cats;
+------+------+-----------+
| c_id | p_id | c_name |
+------+------+-----------+
| 1 | 1 | cats 1 |
| 2 | 1 | cats 2 |
| 3 | 1 | cats 3 |
+------+------+-----------+
Meta Table
mysql> SELECT * FROM meta;
+------+------+------+---------+-------------+-------+
| m_id | p_id | c_id | name | description | costs |
+------+------+------+---------+-------------+-------+
| 1 | 1 | 1 | Abhijit | description | 100 |
| 2 | 1 | 1 | Abhijit | description | 200 |
| 3 | 1 | 2 | Abhiji2 | description | 500 |
+------+------+------+---------+-------------+-------+
Transaction Table
mysql> SELECT * FROM transactions;
+------+------+------+---------------------+--------+
| t_id | p_id | m_id | date | amount |
+------+------+------+---------------------+--------+
| 1 | 1 | 1 | 2016-02-16 11:17:06 | 50 |
| 2 | 1 | 1 | 2016-02-16 11:17:06 | 50 |
| 3 | 1 | 2 | 2016-02-16 11:17:06 | 50 |
| 4 | 1 | 2 | 2016-02-16 11:17:06 | 150 |
+------+------+------+---------------------+--------+
I want to sum() for each category costs (from meta table) and amount( from transaction table).
I use:
mysql> SELECT c.*, SUM(t.amount), SUM(m.costs)
FROM cats c
LEFT JOIN meta m ON m.c_id=c.c_id
LEFT JOIN transactions t ON t.m_id=m.m_id
GROUP BY c.c_id;
+------+------+-----------+--------+---------------+--------------+
| c_id | p_id | c_name | add_by | SUM(t.amount) | SUM(m.costs) |
+------+------+-----------+--------+---------------+--------------+
| 1 | 1 | Abhijit | 1 | 100 | 400 |
| 2 | 1 | Abhiji2 | 1 | 200 | 500 |
+------+------+-----------+--------+---------------+--------------+
It's wrong. The Costs of cats id 1 is 300 but here I got 400
I Want Get Return From Query Like This:
+------+------+-----------+--------+---------------+--------------+
| c_id | p_id | c_name | add_by | SUM(t.amount) | SUM(m.costs) |
+------+------+-----------+--------+---------------+--------------+
| 1 | 1 | Abhijit | 1 | 100 | 300 |
| 2 | 1 | Abhiji2 | 1 | 200 | 500 |
+------+------+-----------+--------+---------------+--------------+
I think you had a typo (or error) in one of your JOIN conditions. I think you intended your original query to be this:
SELECT c.*, SUM(t.amount), SUM(m.costs)
FROM cats c
LEFT JOIN meta m ON m.c_id = c.c_id
LEFT JOIN transactions t ON t.m_id = m.c_id
GROUP BY c.c_id;
Note carefully ON t.m_id = m.c_id, which agrees with your expected output. In any case, I reworked your query as follows:
SELECT c.c_id, c.p_id, c.c_name, t2.transactionCosts, t1.metaCosts
FROM cats c
LEFT JOIN
(
SELECT c_id, SUM(costs) AS metaCosts
FROM meta
GROUP BY c_id
) t1
ON c.c_id = t1.c_id
LEFT JOIN
(
SELECT m_id, SUM(amount) AS transactionCosts
FROM transactions
GROUP BY m_id
) t2
ON c.c_id = t2.m_id
WHERE t2.transactionCosts IS NOT NULL OR t1.metaCosts IS NOT NULL;
The first subquery computes the meta total for each c_id, and the second subquery computes the transaction total for each m_id. These results are then both joined together with the cats table to get your final result.
Follow the link below for a running demo:
SQLFiddle
the problem is you select c.* but only group by c_id, in this case you have 2 options. window function or subquery.
via over(partition by):
SELECT c.*,
SUM(t.amount)over(partition by c.c_id) as amount,
SUM(m.costs)over(partition by c.c_id) as cost
FROM con_cats c
LEFT JOIN meta m ON m.c_id=c.c_id
LEFT JOIN transactions t ON t.m_id=m.m_id;
via subquery:
select a.*,b.amount,b.costs from con_cats a
inner join
(SELECT c.c_id, SUM(t.amount) as amount, SUM(m.costs) as costs
FROM con_cats c
LEFT JOIN meta m ON m.c_id=c.c_id
LEFT JOIN transactions t ON t.m_id=m.m_id
GROUP BY c.c_id) b
on a.c_id = b.c_id;

SQL query statement table joins

I have a problem with a SQL query and the resultset being returned not being what I expected.
I have these three tables that I am trying to relate.
events_detail
__________________
| ID | start_date |
| 1 | 2012-08-09 |
| 2 | 2013-02-13 |
| 3 | 2012-12-12 |
| 4 | 2013-01-21 |
| 5 | 2012-12-25 |
-------------------
where ID is the primary key
events_category_relationship
__________________________
| ID | event_id | cat_id |
| 1 | 1 | 1 |
| 2 | 2 | 4 |
| 3 | 3 | 2 |
| 4 | 4 | 2 |
| 5 | 5 | 3 |
--------------------------
where ID is primary key
events_category_detail
__________________________________
| ID | name | description |
| 1 | Europe | Kings and castles! |
| 2 | USA | Freedoms |
| 3 | China | Made in China |
| 4 | UK | Big Brother |
------------------------------------
where ID is primary key
What I need to do is grab only 1 event from each category and sorted by date of earliest appearance. So what I should expect in my result is the following
Result Set
________________________________________________________________
| e_id | start_date | c_id | category_name | category_desc |
| 1 | 2012-08-09 | 1 | Europe | Kings and castles! |
| 3 | 2012-12-12 | 2 | USA | Freedoms |
| 5 | 2012-12-25 | 3 | China | Made in China |
| 2 | 2013-02-13 | 4 | UK | Big Brother |
-----------------------------------------------------------------
My SQL query that I tried looks like this
SELECT e.id, e.start_date, c.category_name, c.category_desc
FROM events_detail e
JOIN events_category_relationship r ON r.event_id = e.id
JOIN events_category_detail c ON c.id = r.cat_id
ORDER BY date(e.start_date)
This just joins the 3 tables and returns the result in order by date. What I am stuck on is making it so that only one of each category is displayed like the desired result set above. I have tried using DISTINCT c.category_name and GROUP BY c.category_name, but none of them works.
Any help or advice will be greatly appreciated.
You will want to use a subquery to get the min(start_date) for each name and description. You will then use this result to join back to your events_details table to get the id associated with the data in the subquery:
SELECT e.id,
d.start_date,
d.name,
d.description
FROM events_detail e
INNER JOIN
(
select min(e.start_date) Start_date,
c.name,
c.description
from events_detail e
INNER JOIN events_category_relationship r
ON r.event_id = e.id
INNER JOIN events_category_detail c
ON c.id = r.cat_id
group by c.name, c.description
) d
on e.start_date = d.Start_date
ORDER BY date(e.start_date)
See SQL Fiddle with Demo

Categories