Mysql query from one table with different conditions - mysql

I would like to calculate every item's vote count for this table:
user item vote
--------------------------------
4 1 left
4 2 left
2 2 right
3 2 left
1 3 right
The result must be like this:
item | left_vote | right_vote
1 1 0
2 2 1
3 0 1
I've tried to use queries like this:
SELECT t.item, count(vote) as left_count, t.right_count from (SELECT count(vote) as right_count, item from view where vote = 'right') as t, view where vote = 'left';
But it doesn't work. I think that I have to use join with subquery.
Is it real with mysql?

In MySQL you can just use conditional aggregation:
select item, sum(vote = 'left') as left_vote, sum(vote = 'right') as right_vote
from votes v
group by item;
You don't need a join or subquery for this.

Related

Getting percentage of total in SQL with two joins

So I'm trying to do something that I think should be fairly simple with SQL. But I'm having a hard time figuring it out. Here is the format of my data:
One table with user information, let's call it User:
ID name_user Drive_Type
1 Tim Stick shift
2 Jim Automatic
3 Bob Automatic
4 Lisa Stick shift
Then I have one table used for the join, let's call it Join_bridge:
user_ID car_has_ID
1 12
2 13
3 14
4 14
And one table with car information, let's call it Car:
car_ID name
12 Honda
13 Toyota
14 Ford
Then what I want is something that looks like this with the total number of Ford's that are stick shift and the percentage
name Total percentage
Ford 1 25%
I have tried the following, which gets the total right, but not the percentage:
select Drive_Type,
name,
count(Drive_Type) as Total,
(count(Drive_Type) / (select count(*)
from User
join Join_bridge
on User.ID = user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
) * 100.0 as Percent
from User
join Join_bridge
on User.ID = Join_bridge.user_ID
join Car
on Car.car_ID = Join_bridge.car_has_ID
where name = 'Ford' and Drive_Type = "Automatic"
;
What am I missing? Thanks.
See this SQL Fiddle with the query - the trick is to SUM over CASE that returns 1 for rows you look for and 0 for the rest in order to calculate "Total" at the same time you can also count all rows to calculate percentage.
Here's the SQL query:
SELECT
'Ford' name,
SUM(a.ford_with_stack_flag) Total,
100.0 * SUM(a.ford_with_stack_flag) / COUNT(*) percentage
FROM (
SELECT
Car.name,
(CASE WHEN User.Drive_Type = 'Stick Shift' and Car.name = 'Ford' THEN 1 ELSE 0 END) ford_with_stack_flag
FROM User
JOIN Join_bridge on User.ID = Join_bridge.user_ID
JOIN Car ON Car.car_ID = Join_bridge.car_has_ID
) a
Compute percent and join to Car. Window functions are supported in MySql 8.0
select c.car_ID, c.name, p.cnt, p.Percent
from car c
join (
select car_has_ID, u.Drive_Type,
count(*) cnt,
count(*) / count(count(*)) over() Percent
from Join_bridge b
join user u on u.ID = b.user_ID
group by b.car_has_ID, u.Drive_Type
) p on p.car_has_ID = c.car_ID
where c.name = 'Ford' and p.Drive_Type='Stick shift';
db<>fiddle

Count items with certain state even if the amount is zero in MySQL

I have a two tables - items and its states. Each item has only one state. I need to get list of states with counted items of that state, but only from certain ower. When I remove WHERE part I get list of all states, but with it I get only some of states, because there are not at all same states used in items with owner no.7. How to get all of states even if it's amount is zero?
Only solution I have is use subquery instead of COUNT(i.id), but that would be much more slower and I am afraid of performance.
My tables:
ID STATE NAME
== ==========
1 New
2 Used
3 Archived
4 Unknown
ID STATE ITEM NAME OWNER
== ===== ========= =====
1 1 ABC 7
2 2 DEF 6
3 3 GHI 7
4 1 JKL 7
My query:
SELECT
s.id,
s.name,
COUNT(i.id) AS count
FROM
b_items_states s
LEFT JOIN
b_items i ON i.state = s.id
WHERE
i.owner = 7
GROUP BY
s.id
My result:
ID NAME COUNT
== ==== =====
1 New 2
3 Archived 1
My expected result:
ID NAME COUNT
== ==== =====
1 New 2
2 Used 0
3 Archived 1
4 Unknown 0
Move the filtering condition inside the JOIN, as in:
SELECT
s.id,
s.name,
COUNT(i.id) AS count
FROM
b_items_states s
LEFT JOIN
b_items i ON i.state = s.id AND i.owner = 7
GROUP BY
s.id
If you place the filtering condition in a WHERE statement, you are implicitly converting the outer join into an inner join, without even noticing.
Impaler has a correct answer
You can also express this as a correlated subquery:
SELECT s.id, s.name,
(SELECT COUNT(*)
FROM b_items i
WHERE i.state = s.id AND
i.owner = 7
) as count
FROM b_items_states s;
In particular, this can take advantage of an index on b_items(state, owner). By avoiding the outer aggregation, it should have better performance.

Count rows in table with certain value in other table

I need a quick way to find the Number of items in a table. The items are linked to an other table. Table 1 is products and table 2 is orders.
Orders contains a paid status (1 or 0).
Orders table example:
id paid
1 0
2 1
Products table example:
id orderid type
1 1 5
2 1 5
3 1 3
4 2 5
5 2 5
6 2 3
Products contains a id (orderid) that refers to the order and a type. So i need the number of products where type = 5 and paid = 1 in the orders table.
What is the best and fastest way to archieve this?
So I need all the paid products with type 5. The result should be '2'.
you can use join like this,
SELECT COUNT(*) AS num_rows
FROM products
LEFT JOIN orders ON orders.id = products.orderid
WHERE type = 5 AND paid = 1
One way is to use a join statement. Making some assumptions about your schema, the following should work:
SELECT COUNT(p.`id`) FROM `products_table` p
LEFT JOIN `orders_table` o ON p.`orderid` = o.`id`
WHERE o.`paid` = 1
AND p.`type` = 5

MySQL create a sum number if row exists in table

I have a statement like so:
select * from category a
inner join category b on a.row=b.relatedRow
inner join category c on b.row=c.relatedRow where a.row=?
I would like to get the number of "levels" like so:
If a has rows, level=1, If b has rows, level=2, If c has rows, level=3.
How can I do this?
Example
row, relatedRow
1,null
2,1
3,2
4,3
5,2
6,5
So, 1 is not related to any row, 2 is related to 1, 3 is related to 2 and so on...
If row=1, level 1 exists since 1 exists
level 2 exists since 2 is related to 1
level 3 exists since 3 and 5 is related to 2
level 4 exists since 6 is related to 5 and 4 is related to 3
Therefore the this tree goes down 4 levels.
try with something along the lines:
select
sum(case when not b.relatedRow is null then 1 else 0 end) as level1_total
sum(case when not c.relatedRow is null then 1 else 0 end) as level2_total
from category a
left join category b on a.row=b.relatedRow
left join category c on b.row=c.relatedRow where a.row=?
of course, you can modify the conditions in the case to suit your definition of has rows
select
max(
case
when c.relatedRow is not null then 3
when b.relatedRow is not null then 2
else 1
end
) as "levels"
from
A a
left outer join B b on b.relatedRow = a.row
left outer join C on c.relatedRow = b.row
Now seeing the edit to the question, I hope you see this pattern can be extended to a 4th level and beyond. If you add a where clause to do any filtering make sure that you only add conditions against A or you'll mess up the outer joins.

Case or if on mySQL query

I have tables like:
timeline
id data_id pattern
1 1 add_card
2 1 add_post
3 2 upd_card
4 2 upd_post
card
id name parent
1 cname1 1
2 cname2 4
3 cname3 5
post
id name parent
1 pname1 8
2 pname2 9
3 pname3 3
i need form result
** result timeline**
id data_id pattern name parent
1 1 add_card cname1 1
2 1 add_post pname1 8
3 2 upd_card cname2 4
4 2 upd_post pname2 9
My ideas in only IF or CASE statement
SELECT id, data_id, pattern
(CASE parent
WHEN ‘add_card’ THEN (SELECT name.card,parent.card FROM card WHERE data_id.timeline = id.card)
WHEN ‘add_card’ THEN (SELECT name.post,parent.post FROM post WHERE data_id.timeline = id.post)
END)
FROM timeline
but this is not right syntax, this is just my guess how it would look!
Use LEFT OUTER JOINs to selectively join in the card or post table and COALESCE to get the resulting parent/name info in one column:
SELECT timeline.id, timeline.data_id, timeline.pattern,
COALESCE (post.name, card.name) AS name,
COALESCE (post.parent, card.parent) AS parent
FROM timeline
LEFT OUTER JOIN card ON timeline.data_id = card.id
AND timeline.pattern IN ('add_card', 'upd_card')
LEFT OUTER JOIN post ON timeline.data_id = post.id
AND timeline.pattern IN ('add_post', 'upd_post')
You can see the result here: http://sqlfiddle.com/#!2/76d8cb/1/0
The result doesn't match your example result, but your example result appears to be inconsistent with your data, so I think what this does is what you meant. (Update: I've now edited your example results to be consistent with your example tables.)
You appear to be checking for the same value and doing 2 different things.
However I think you probably want something like this:-
SELECT timeline.id, timeline.data_id, timeline.pattern
CASE
WHEN timeline.pattern = 'add_card' THEN card.name
WHEN timeline.pattern = 'add_card' THEN post.name
ELSE NULL
END,
CASE
WHEN timeline.pattern = 'add_card' THEN card.parent
WHEN timeline.pattern = 'add_card' THEN post.parent
ELSE NULL
END
FROM timeline
LEFT OUTER JOIN card timeline.data_id = card.id
LEFT OUTER JOIN post timeline.data_id = post.id