mysql ) How to print using group by, including when count is 0 - mysql

When I have such a table,
I want to get the result of a select statement like the picture below.
However, when count is 0, the row is omitted.
This makes me confused.
How can I modify this SQL statement to print with count = 0 rows?
SELECT ca.syear, COUNT(cc.joined),cc.joined
FROM schedules AS cc JOIN
schedule_assignment AS ca
ON ca.idx = cc.assignment
where student = 1 AND cc.joined = 1
GROUP BY ca.syear;
Sorry, my reputation is under 10 so I can't show the image right away. :(

I think you just want a LEFT JOIN:
SELECT sa.syear, COUNT(cc.joined)
FROM schedule_assignment sa LEFT JOIN
schedules s
ON ca.idx = cc.assignment AND
s.student = 1 AND
s..joined = 1
GROUP BY sa.syear;
I did not include joined in the result set It is always 1, so that does not seem useful. You can include it: 1 as joined, if you really want it.

Related

MySQL DISTINCT returning not so distinct results

Good day,
I have a small issue with MySQL Distinct.
Trying the following query in my system :
SELECT DISTINCT `booking_id`, `booking_ticket`, `booking_price`, `bookingcomment_id`, `bookingcomment_message` FROM `mysystem_booking`
LEFT JOIN `mysystem_bookingcomment` ON `mysystem_booking`.`booking_id` = `mysystem_bookingcomment`.`bookingcomment_link`
WHERE `booking_id` = 29791
The point is that there are bookings like 29791 that have many comments added.
Let's say 10. Then when running the above query I see 10 results instead of one.
And that's not the way DISTINCT supposes to work.
I simply want to know if there are any comments. If the comment ID is not 0 then there is a comment. Of course I can add COUNT(blabla) as comment_number but that's a whole different story. For me now I'd like just to have this syntax right.
You may try aggregating here, to find which bookings have at least a single comment associated with them:
SELECT
b.booking_id,
b.booking_ticket,
b.booking_price
FROM mysystem_booking b
LEFT JOIN mysystem_bookingcomment bc
ON b.booking_id = bc.bookingcomment_link
WHERE
b.booking_id = 29791
GROUP BY
b.booking_id
HAVING
COUNT(bc.bookingcomment_link) > 0;
Note that depending on your MySQL server mode, you might have to also add the booking_ticket and booking_price columns to the GROUP BY clause to get the above query to run.
You can try below - using a case when expression
SELECT DISTINCT `booking_id`, `booking_ticket`, `booking_price`, `bookingcomment_id`,
case when `bookingcomment_message`<>'0' then 'No' else 'Yes' end as comments
FROM `mysystem_booking`
LEFT JOIN `mysystem_bookingcomment` ON `mysystem_booking`.`booking_id` = `mysystem_bookingcomment`.`bookingcomment_link`
WHERE `booking_id` = 29791

Create array that has data from two tables

I would like to create a table where all data from "treatment_parameters" is used and where parameter_fk=47. Additionally, the table should also include only the count(*) of rows from "treatment_log" where the missed_treatment=' '.
So the table should project out all data from "treatment_parameters" with parameter_fk=47 and the number of rows in "treatment_log" where missed_treatment=' '.
Should also note that: t.id = l.treatment_fk
I have attempted this, however it is partially successful. The table displays all required data, however it does not display every row from "treatment_paratmers" with parameter_fk=47. (There should be two different rows)
SELECT t.id,t.parameter_fk,t.course_name,t.room, t.protocol,t.navigation,t.area,t.coil,t.number_of_treatments,t.motor_threshold,t.threshold_multiplier,t.target_threshold,t.about,t.date,t.created_at, COUNT(*) AS completed_treatments
FROM treatment_parameters t
LEFT JOIN treatment_log l
ON t.id = l.treatment_fk
WHERE t.parameter_fk = 47
AND l.missed_treatment=' '
Below you can see the result of the above SQL query:
Image 1
However, you can see that when the SQL query is modified, there is more than just one row for paratmer_fk=47
SELECT t.id,t.parameter_fk,t.course_name,t.room, t.protocol,t.navigation,t.area,t.coil,t.number_of_treatments,t.motor_threshold,t.threshold_multiplier,t.target_threshold,t.about,t.date,t.created_at
FROM treatment_parameters t
WHERE t.parameter_fk = 47
Image 2
So we need to show both rows, and also have the count(*) for missed_treatment=' ' effect both rows.
Below you can find the entire list from treatment_log.
treatment_log
So you need to self-join:
SELECT t.id,t.parameter_fk,t.course_name,t.room, t.protocol,t.navigation,t.area,
t.coil,t.number_of_treatments,t.motor_threshold,t.threshold_multiplier,t.target_threshold,
t.about,t.date,t.created_at, aa.t_count AS completed_treatments
FROM treatment_parameters t
left join (select treatment_fk, count(*) as t_count from treatment_log l
where l.missed_treatment=' ' group by treatment_fk) aa on aa.treatment_fk=t.id
WHERE t.parameter_fk = 47

How to optimize MySQL multiple tables select queries?

My MySQL query code likes as shown below, and there are about several thousands of records in the table, by now below SQL executes about 5 minutes and more. I am looking for ways to optimize it so that it takes less time to execute. Thank you!
SELECT `m`.`id`,
`m`.`id`,
`tr`.`name`,
`m`.`m_date`,
`t1`.`t_name` AS home,
`t2`.`t_name` AS away,
`m`.`score1`,
`m`.`score2`,
`cw1`.`tid` AS tid1,
`cw2`.`tid` AS tid2,
`o1`.`odds` AS odds1,
`o2`.`odds` AS odds2,
`m`.`m_time`
FROM `jos_bl_match` AS `m`
LEFT JOIN `jos_bl_matchday` AS `md` ON (`md`.`id` = `m`.`m_id`)
LEFT JOIN `jos_bl_seasons` AS `s` ON (`s`.`s_id` = `md`.`s_id`)
LEFT JOIN `jos_bl_tournament` AS `tr` ON (`tr`.`id` = `s`.`t_id`)
LEFT JOIN `jos_bl_teams` AS `t1` ON (`m`.`team1_id` = `t1`.`id`)
LEFT JOIN `jos_bl_teams` AS `t2` ON (`m`.`team2_id` = `t2`.`id`)
LEFT JOIN `jos_vuvuzelaodds_odds` AS `o1` ON (`o1`.`m_id` = `m`.`id`)
AND `o1`.`market_id` = 1
AND `o1`.`bookmaker_id` = 1
LEFT JOIN `jos_vuvuzelaodds_odds` AS `o2` ON (`o2`.`m_id` = `m`.`id`)
AND `o2`.`market_id` = 1
AND `o2`.`bookmaker_id` = 2
LEFT JOIN `jos_cwtags_tags` AS `cw1` ON (`cw1`.`item_id` = `o1`.`m_id`)
LEFT JOIN `jos_cwtags_tags` AS `cw2` ON (`cw2`.`item_id` = `o2`.`m_id`)
WHERE `m`.`published` = 1
AND `s`.`published` = '1'
AND `tr`.`published` = '1'
AND `s`.`s_id` = 869
AND `m`.`m_played` = '1'
AND `m`.`m_date` > 2013-01-01
AND `o1`.`odds` != ''
AND `o2`.`odds` != ''
AND `cw1`.`cat_id` = 19
AND `cw2`.`cat_id` = 21
ORDER BY `m`.`m_date`,
`md`.`id`,
`s`.`s_id`,
`tr`.`id` DESC LIMIT 0, 15
"Normalize, but don't over-normalize."
Some composite indexes you may be missing...
jos_bl_match: INDEX(m_played, published, m_date)
The columns need to be in that order. That will more quickly start the filtering.
The following should speed up their JOINs:
jos_vuvuzelaodds_odds: INDEX(market_id, bookmaker_id, m_id)
jos_cwtags_tags: INDEX(cat_id, item_id)
It seems like those last two indexes could (should) be the PRIMARY KEY. Are they?
Some (perhaps all) of the LEFT JOINs may as well be INNER JOINs; did you consider that?
Please provide EXPLAIN SELECT.
Without having access to the database it is a little hard to tell. This seems to be a lot of data to only pull 15 records. Are you sure you need to pull the data this way?
Probably the best route:
Optimize your database as below.
Make a flat database view of all the games that has the fields that you need. A static table will be much faster but you would need to set up updates with triggers which is beyond the scope of this answer but the process is similar
Write queries against the
view.
You are not selecting any fields from the seasons table in your select. I used the field from the jos_bl_matchday table.
This should get you started. You can also use conditionals in your select statements
(IF value = 1, table.field, null) as yadda
instead of joining one table over and over again but you would have to experiment.
CREATE VIEW allTheGames AS SELECT
`m`.`id` as id,
`md`.`s_id` as seasonId,
`tr`.`name` as name,
`m`.`m_date` as m_date,
`t1`.`t_name` AS home,
`t2`.`t_name` AS away,
`m`.`score1` as score1,
`m`.`score2` as score2,
`cw1`.`tid` AS tid1,
`cw2`.`tid` AS tid2,
`o1`.`odds` AS odds1,
`o2`.`odds` AS odds2,
`m`.`m_time` as m_time
FROM `jos_bl_match` AS `m`
LEFT JOIN `jos_bl_matchday` AS `md` ON (`md`.`id` = `m`.`m_id`)
LEFT JOIN `jos_bl_tournament` AS `tr` ON (`tr`.`id` = `s`.`t_id`)
LEFT JOIN `jos_bl_teams` AS `t1` ON (`m`.`team1_id` = `t1`.`id`)
LEFT JOIN `jos_bl_teams` AS `t2` ON (`m`.`team2_id` = `t2`.`id`)
LEFT JOIN `jos_vuvuzelaodds_odds` AS `o1` ON (`o1`.`m_id` = `m`.`id`) AND `o1`.`market_id` = 1 AND `o1`.`bookmaker_id` = 1
LEFT JOIN `jos_vuvuzelaodds_odds` AS `o2` ON (`o2`.`m_id` = `m`.`id`) AND `o2`.`market_id` = 1 AND `o2`.`bookmaker_id` = 2
LEFT JOIN `jos_cwtags_tags` AS `cw1` ON (`cw1`.`item_id` = `o1`.`m_id`)
LEFT JOIN `jos_cwtags_tags` AS `cw2` ON (`cw2`.`item_id` = `o2`.`m_id`)
WHERE `m`.`published` = 1 AND `s`.`published` = '1' AND `tr`.`published` = '1'
AND `m`.`m_played` = '1'
AND `o1`.`odds1` != '' AND `o2`.`odds2` != ''
Then query it with:
select * from allTheGames
WHERE season_id = 869 AND m_date > 2013-01-01 AND tid1 = 19 AND tid2 = 21
Steps to optimize:
Figure out exactly which data you want out of this query and why:
Is this a custom report? A web page? do you need to have all of this data at once or would it make more sense to have the user drill down?
How often is the query run? Once a minute? Once a day?
How many records are in each table? Your view should reflect this "game object"
Your database:
Run "explain" against this query http://dev.mysql.com/doc/refman/5.7/en/explain.html.
It will show all of the work the database is doing, the query execution plan, and how many records it is looking in to do it. This usually happens quickly: it does not actually execute the query.
See if you can put the database on SSD drives or even RAM
Your table structure:
Make sure that you have indexes on all of the fields that you are searching on. There are many ways to optimize this.
Use "explain" to be sure MySQL is able to use indexes.
If there are really only 2 markets in the jos_vuvuzelaodds_odds table consider making 2 fields.
Good luck!

SQL RAND Query selecting all records and not using joined table

Please can someone talk me through a query that I have inherited through a website I am developing.
The query is returning a random group of 5 products based on the category number 56. There is an issue with the query because it is not restricting the selection based on the product on web and product archive conditions.
AND p.product_OnWeb = 1
AND p.product_Archive = 0
The above lines in the query aren't being adhered to. Instead the query is including all products even when they are marked as p.product_Archive=1 (Archived) and p.product_OnWeb = 0 (Not Online)
If someone could point out where I need to make a change I'd be grateful.
The query in full is:-
SELECT c.prdt_cat_rel_Product_ID,
ROUND(RAND() * x.m_id) 'rand_ind'
FROM tbl_prdtcat_rel c,
tbl_products p,
(SELECT MAX(t.prdt_cat_rel_Cat_ID) 'm_id'
FROM tbl_prdtcat_rel t) x
WHERE c.prdt_cat_rel_Cat_ID = 56
AND p.product_OnWeb = 1
AND p.product_Archive = 0
ORDER BY rand_ind
LIMIT 3
Thankyou
First, to make it easier, convert the query to the newer join syntax
SELECT c.prdt_cat_rel_Product_ID, ROUND(RAND() * x.m_id) 'rand_ind'
FROM tbl_prdtcat_rel c,
JOIN tbl_products p ON p.??? = c.???
JOIN (
SELECT MAX(t.prdt_cat_rel_Cat_ID) 'm_id' FROM tbl_prdtcat_rel t
) x ON 1=1
WHERE c.prdt_cat_rel_Cat_ID = 56
AND p.product_OnWeb = 1
AND p.product_Archive = 0
ORDER BY rand_ind
LIMIT 3
You can see that the query doesn't know how to select match records from P based on C, so it grabs them all.. You need to specify how to match tbl_prdtcat records with tbl_product records (replace the ??? in the above with the appropriate fields)
I am guessing each product in p has some sort of field indicating which category it belong to, use this field to match and your query should work...

MySQL - How can I use left join to get exactly one row from a joined table in the on clause?

I hope the question is clear enough.
Here's my query:
SELECT
'Total Complete' AS name, count(c.id) AS amnt
FROM
lanes AS c
LEFT JOIN leads AS l
ON ((c.carrier_mcn = l.authority_number
AND l.authority_type = 'mc')
OR c.carrier_mcn = l.mcn
OR c.carrier_mcn = CONCAT('MC', l.authority_number))
AND c.carrier_mcn <> 0
LEFT JOIN lanes_emails AS cem1
ON cem1.cid = c.id AND cem1.deleted = 0
LEFT JOIN lanes_emails AS cem2
ON cem1.cid = cem2.cid AND cem2.deleted = 0 AND cem2.id < cem1.id
LEFT JOIN lanes_equipment AS ceq1
ON ceq1.cid = c.id AND ceq1.deleted = 0
LEFT JOIN lanes_equipment AS ceq2
ON ceq1.cid = ceq2.cid AND ceq1.deleted = 0 AND ceq2.id < ceq1.id
LEFT JOIN lanes_service_area AS csa1
ON csa1.cid = c.id AND AND flag = 2 csa1.deleted = 0
LEFT JOIN lanes_service_area AS csa2
ON csa1.cid = csa2.cid AND AND flag = 2 csa1.deleted = 0 AND csa2.id < csa1.id
WHERE
c.carrier_mcn IS NOT NULL and c.carrier_mcn <> ''
AND c.broker_mcn IS NOT NULL and c.broker_mcn <> ''
AND c.fax IS NOT NULL and c.fax <> ''
AND cem2.id IS NOT NULL
AND ceq2.id IS NOT NULL
AND csa2.id IS NOT NULL
Now what I'm trying to do here is get the total count of rows from "lanes" where it has at least one email, equipment, service area, mc number, or fax number. There are two MC Numbers, but they're both on the same table; the main issue is that there can be unlimited emails, equipment, and service areas.
In this case, I starting writing out an article about this issue and discovered another similar problem:
MySQL: Can I do a left join and pull only one row from the join table?
So I tried it out and implemented it into my query. Unfortunately, it didn't work the way I had planned.
It would work fine if I had more than one row; it would get the correct count, ignoring the fact that I had multiple emails or whatnot, but it would NOT work if there was only one. Instead, it returned no results.
No, I cannot use GROUP BY to get the results I need; I've tried GROUP BY, I love using it when returning full result lists, but if I'm trying to return counts GROUP BY repeatedly messes me up.
Is there any way to force a join in mysql to return just one row without using a subquery? I only need to know if there is one item from any of these other tables, I don't need to return everything, and I don't know of a way to program it to work through the WHERE clause.
I need this query for a bar chart. I've actually got several of these queries; this one is the one that requires all results, but I have others for no requirements, at least one email, at least one mc number, etc., but if I can get this working I can get the rest working.
You could use an exists clause:
where exists
(
select *
from lanes_emails AS cem1
where cem1.cid = c.id
and cem1.deleted = 0
)
and exists
...