Count 2 columns for occurrence - mysql

So basically, I require a query that will return display name, amount of kills and amount of deaths.
I have two tables that I need to pull from.
The two tables are
player
id | name
2334324 | user
4353454 | user2
where id is their unique identifier and name is their display name.
The second table is:
player_kill
id | killer | victim |
1 | 2334324 | 4353454 |
2 | 2334324 | 4353454 |
3 | 4353454 | 2334324 |
where killer / victim columns contain the unique identifier of the player table.
I'd like to be able to count the occurrences of player id in the killer and victim so that the query returns:
name | kills | deaths
user | 2 | 1
user2| 1 | 2
where the number under kills would be the amount of occurrences the playerid has in the killer column and same for deaths
Hope I provided enough information.
What I have so far:
SELECT `player`.`name`, COUNT(DISTINCT `player_kill`.`id`) as `kills`, COUNT(DISTINCT `player_kill`.`id`) as `deaths`
FROM `player`
LEFT JOIN `player_kill` ON `player`.`id`=`player_kill`.`killer`
LEFT JOIN `player_kill` ON `player`.`id`=`player_kill`.`victim`
WHERE `player`.`id` = `player_kill`.`killer` AND `player`.`id` = `player_kill`.`victim`
GROUP BY `player`.`id`;

Try
SELECT
p.name,
count(distinct pk1.id) as kills,
count(distinct pk2.id) as deaths
FROM player p
LEFT JOIN player_kill pk1 ON pk1.killer = p.id
LEFT JOIN player_kill pk2 ON pk2.victim = p.id
group by p.name
http://sqlfiddle.com/#!9/649504/15/0

See if this works:
SELECT `player`.`name`,
COUNT(DISTINCT k.`id`) as `kills`,
COUNT(DISTINCT v.`id`) as `deaths`
FROM `player`
LEFT JOIN `player_kill` AS k ON `player`.`id` = k.`killer`
LEFT JOIN `player_kill` AS v ON `player`.`id` = v.`victim`
GROUP BY `player`.`id`;
If not, then we may need to make the COUNTs into subqueries.

Related

How to get multiple counts with multiple joins using one SQL query?

There is an error in my query and I would like some help. I have three tables
Rooms{id,number,name,type(ECO/LUX),active(0/1)}
Men{passport,roomid,status(YOUTH/ADULT)}
Women{passport,roomid,status(YOUTH/ADULT)}
**In each room there can be more than one woman or man.
I want to count how many women and men have the same room with roomid in (1,2,3), status='ADULT', type='LUX' and active=1. Therefore I need a result like this:
+----+--------+-----------+----------+------------+
| id | number | name | CountMen | CountWomen |
+----+--------+-----------+----------+------------+
| 1 | 23 | 1st suite | 2 | 4 |
| 3 | 4 | 2nd suite | 1 | 2 |
+----+--------+-----------+----------+------------+
SELECT id,number,name,
sum(case when Men.status='ADULT' then 1 else 0 end) as CountMen,
sum(case when Women.status='ADULT' then 1 else 0 end) as CountWomen
FROM Rooms left join Men
on Rooms.id=Men.roomid
left join Women on Room.id=Women.roomid where
(type='LUX') and (active=true) and (id in (1,2,3))
group by id;
The problem is that I get sometimes wrong results in the counters.
In a left join, conditions on the second table need to be in the on clause. It would help if you qualified all column names in the query.
However your problem is because you are getting a Cartesian product between the gender tables. This is definitely a case where gender segregation is not a good thing. You should have just one table for people (and this doesn't even bring up other issues with defining binary genders).
SELECT r.id, r.number, r.name,
(SELECT COUNT(*)
FROM men m
WHERE m.status = 'ADULT' AND r.id = m.roomid
) as CountMen,
(SELECT COUNT(*)
FROM women w
WHERE w.status = 'ADULT' AND r.id = w.roomid
) as CountWomen
FROM Rooms r
WHERE r.type = 'LUX' AND r.active = true AND r.id IN (1, 2, 3);
However, you should fix your data model so you have people rather than segregated gender tables.

Trying to join two tables, one ordered to get flagged row first

I have these two tables:
prt_gebouw
id | name
----+------------
1 | Building A
2 | Building B
3 | Building C
prt_image
id | building_id | name | is_primary
----+---------------+-----------+------------
1 | 1 | img1.jpg | 0
2 | 1 | img2.jpg | 0
3 | 2 | img3.jpg | 0
4 | 1 | img4.jpg | 1
5 | 2 | img5.jpg | 1
As you can see here, some buildings have more than one image and some have none. When a building has one image or more, only one image can be marked as primary; can, for this is not mandatory.
Now, what I am trying to do is list all buildings (each building once) and join this with the images table, preferrably the primary image, empty cells if no image can be found.
So first I tried this:
SELECT
pgb.id,
pgb.name,
img.id AS image_id,
img.name AS image_name,
img.is_primary AS is_primary
FROM
prt_gebouw pgb
LEFT JOIN prt_image img ON pgb.id = img.object_id AND img.kind = 'object'
GROUP BY pgb.id
ORDER BY img.is_primary DESC, pgb.id ASC;
I suspect that the grouping is done before the ordering, because the wrong image is joined with each building that has more than one image ("wrong" being here: not the primary one).
Then I tried:
SELECT
pgb.id,
pgb.name,
img.id AS image_id,
img.name AS image_name,
img.is_primary
FROM
prt_gebouw pgb
LEFT JOIN (SELECT * FROM prt_image ORDER BY is_primary DESC) AS img ON img.object_id = pgb.id
ORDER BY pgb.id ASC;
I was hoping that for each building the primary image would be listed first, but not so. I suspect this is also the problem in the previous query, but is it?
And, more importantly, how can I solve this?
I think a correlated subquery might be easier for what you want:
select pgb.*,
(select i.id
from prt_image i
where i.object_id = pgb.id and i.kind = 'object'
order by is_primary desc
limit 1
) as img_id
from prt_gebouw pgb;
If you want the other fields from the image, join them in afterwards:
select pgb.*, i.* -- I'm using `*` for inconvenience; list the columns here
from (select pgb.*,
(select i.id
from prt_image i
where i.object_id = pgb.id and i.kind = 'object'
order by is_primary desc
limit 1
) as img_id
from prt_gebouw pgb
) pgb left join
prt_image i
on pgb.img_id = i.id;

get percentage along with group by statement

I am using a join query to get data grouped by titles where titles are stored in another table with user id
so the query is like following
SELECT title, COUNT(title) as cnt FROM users_titles
JOIN users
ON users_titles.uid = users.uid
WHERE users.useractivated = 1
GROUP BY title
Now I wan to add an extra column which will show the percentage of each type like following:
----------------------------------
| title | cnt | percentage |
----------------------------------
| 0 | 23658 | 23.67% |
----------------------------------
| 1 | 53658 | 43.67% |
----------------------------------
some thing like this. How can I achieve this?
You could cross join this query with a query counting the total number of users, and divide the two counts to get a percentage:
SELECT title, COUNT(title) AS cnt, COUNT(title) / total_count AS percentage
FROM users_titles
JOIN users ON users_titles.uid = users.uid
CROSS JOIN (SELECT COUNT(*) AS total_count
FROM users) t
WHERE users.useractivated = 1
GROUP BY title
Note: If you want to represent the number as a percentage from the active users, you'd need to add a WHERE users.useractivated = 1 clause to the inner query too.

Select values based on other value within joined tables SQL

I would like to ask for help for an SQL request that give me values from two tables.
As an example I have one Table orders und one table processing.
I would like to make an report of the orders and the state of processing.
table orders
id | status | div
-------------------
1 | wating_r | div1
2 | closed | div2
3 | closed | div3
-
table processing:
id | order_id | type | date
----------------------------------------
1 | 2 | send_request | 15.01.15
2 | 2 | send_invoice | 30.01.15
3 | 1 | send_request | 01.02.15
4 | 3 | send_request2 | 10.02.15
5 | 3 | send_invoice | 15.02.15
what I would like to get:
order_id | status | date_request | date_request2 | date_invoice
--------------------------------------------------------------------------------
1 | waiting_r | 01.02.15 | NULL | NULL
2 | closed | 15.01.15 | NULL | 30.01.15
3 | closed | NULL | 10.02.15 | 15.02.15
my solution:
select orders.id as order_id, orders.status, IF(processing.type='send_invoice',date_format(processing.date, '%Y-%m-%d'), NULL) as date_invoice, IF(processing.type='send_request',date_format(processing.date, '%Y-%m-%d'), NULL) as date_request, IF(processing.type='send_request2',date_format(processing.date, '%Y-%m-%d'), NULL) as date_request2
from orders
inner join processing on orders.id = processing.order_id
where
case
when orders.status='closed' then processing.type='send_invoice'
when orders.status='waiting_r' then processing.type='send_request'
when orders.status='waiting_2'then processing.type='send_request2'
end
This works fine but with this IF statements I doesn't become the dates from the requests when an invoice was sent - I only get the date of the invoice.
Instead of the case request I tried the following but in this case I have more than one line for every order. When I tried to "group by" I have mixed data.
where
processing.type in ('send_invoice', 'send_request', 'completion_request_send')
You need to left-join the second table to the first three times, like so.
SELECT o.id AS order_id, o.status,
p1.date AS date_request,
p2.date AS date_request2,
p3.date AS date_invoice
FROM orders o
LEFT JOIN processing p1 ON o.id = p1.order_id AND p1.type='send_request'
LEFT JOIN processing p2 ON o.id = p2.order_id AND p2.type='send_request2'
LEFT JOIN processing p3 ON o.id = p3.order_id AND p3.type='send_invoice'
ORDER BY 1,2
This left-join with an id-matching criterion and the specific type choice pulls out the rows you need for each column. Left, as opposed to inner, join, allows the missing values to be shown as null.
Here it is, working. http://sqlfiddle.com/#!9/b8c74/5/0
This is a typical pattern for joining a key/value table where the (id/key) pairs are unique.
Edit Unfortunately it generates duplicate result set rows in situations where there's a duplicate key for a particular value. To deal with that, it's necessary to deduplicate the key/value table (processing) in this case.
This subquery will do that, taking the latest date value.
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
Then you have to use that subquery in the main query. This is where it would be good if MySQL had common table expressions. But it doesn't so things get kind of verbose.
SELECT o.id AS order_id, o.status,
p1.date AS date_request,
p2.date AS date_request2,
p3.date AS date_invoice
FROM orders o
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p1 ON o.id = p1.order_id AND p1.type='send_request'
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p2 ON o.id = p2.order_id AND p2.type='send_request2'
LEFT JOIN (
SELECT type, order_id, MAX(date) AS date
FROM processing
GROUP BY type, order_id
) p3 ON o.id = p3.order_id AND p3.type='send_invoice'
ORDER BY 1,2

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