how to combine two tables and count their values as one? - mysql

I want to get the total numbers of committed crimes when combining the two tables.
But I want to count the numbers for each crime being committed and also display the values of those that has not being committed as 0, How can i achieve this using mysql?
my code:
SELECT count(offense_id)
AS totalnumber,(
select offense_description
from offense
where offense.offense_id = case_crime.offense_id
)as crimeName
from case_crime
group by offense_id

To get a list of all your offenses and the count of registered crimes, you need to use the LEFT JOIN between the two tables .... something like this SQL:
SELECT a.offense_id, a.offense_description, count(b.crime_caseid) as total
FROM offense a LEFT JOIN case_crime b
ON a.offense_id = b.offense_id
group by a.offense_id;
Here is a SQLFiddle to play around with :)

You are looking for a LEFT OUTER JOIN
Something like
SELECT offense.offense_id
, offense_description
, count(case_crime.case_id) as Total_Number
from offense
LEFT OUTER JOIN case_crime ON offense.offense_id = case_crime.offense_id
group by offense.offense_id
I'll admit, I'm a TSQL guy, so I would handle the COUNT(*) returning null when there are no cases with the specified offense using: ISNULL(COUNT(case_crime),0)
Other SQL variants might use COALESCE( COUNT(case_crime), 0)

Related

MySQL - Trying to show results for rows that have 0 records...across 3 columns

There's a lot of Q&A out there for how to make MySQL show results for rows that have 0 records, but they all involve 1-2 tables/fields at most.
I'm trying to achieve the same ends, but across 3 fields, and I just can't seem to get it.
Here's what I've hacked together:
SELECT circuit.circuit_name, county.county_name, result.adr_result, count( result.adr_result ) AS num_results
FROM
(
SELECT cases.case_id, cases.county_id, cases.result_id
FROM cases
WHERE cases.status_id <> "2"
) q1
RIGHT JOIN county ON q1.county_id = county.county_id
RIGHT JOIN circuit ON county.circuit_id = circuit.circuit_id
RIGHT JOIN result ON q1.result_id = result.result_id
GROUP BY adr_result, circuit_name, county_name
ORDER BY circuit_name, county_name, adr_result
What I need to see is a list of ALL circuits in the first column, a list of ALL counties per circuit in the second column, a list of ALL possible adr_result entries for each county (they're the same for every county) in the third column, and then the respective count for the circuit/county/result combination-- even if it is 0. I've tried every combination of left, right and inner join (I know inner is definitely not the solution, but I'm frustrated) and just can't see where I'm going wrong.
Any help would be appreciated!
Here is a start. I can't follow your problem statement completely. For instance, what is the purposes of the cases table? None the less, when you say "ALL" records for each of those tables, I interpret it as a Cartesian product - which is implemented through the derived table in the FROM clause (notice the lack of the JOIN in that clause)
SELECT everthingjoin.circuit_name
, everthingjoin.county_name
, everthingjoin.adr_result
, COUNT(result.adr_result) AS num_results
FROM
(SELECT circuit.circuit_name, county.county_name, result.adr_result,
FROM circuit
JOIN county
JOIN result) AS everthingjoin
LEFT JOIN cases
ON cases.status_id <> "2"
AND cases.county_id = everthingjoin.county_id
LEFT JOIN circuit
ON everthingjoin.circuit_id = circuit.circuit_id
LEFT JOIN result
ON cases.result_id = result.result_id
GROUP BY adr_result, circuit_name, county_name
ORDER BY circuit_name, county_name, adr_result
try this, see if it provides some ideas:
SELECT
circuit.circuit_name
, county.county_name
, result.adr_result
, ISNULL(COUNT(result.*)) AS num_results
, COUNT(DISTINCT result.adr_result) AS num_distinct_results
FROM cases
LEFT JOIN county
ON cases.county_id = county.county_id
LEFT JOIN circuit
ON county.circuit_id = circuit.circuit_id
LEFT JOIN result
ON cases.result_id = result.result_id
WHERE cases.status_id <> "2"
GROUP BY
circuit.circuit_name
, county.county_name
, result.adr_result
ORDER BY
circuit_name, county_name, adr_result

MySql query runs very slow(actually never gives output) without where clause

I have a mysql query and it works fine when i use where clause, but when i donot use
where clause it gone and never gives the output and finally timeout.
Actually i have used Explain command to check the performance of the query and in both cases the Explain gives the same number of rows used in joining.
I have attached the image of output got with Explain command.
Below is the query.
I couldn't figure whats the problem here.
Any help is highly appreciated.
Thanks.
SELECT
MCI.CLIENT_ID AS CLIENT_ID, MCI.NAME AS CLIENT_NAME, MCI.PRIMARY_CONTACT AS CLIENT_PRIMARY_CONTACT,
MCI.ADDED_BY AS SP_ID, CONCAT(MUD_SP.FIRST_NAME, ' ', MUD_SP.LAST_NAME) AS SP_NAME,
MCI.FK_PROSPECT_ID AS PROSPECT_ID, MCI.DATE_ADDED AS ADDED_ON,
(SELECT GROUP_CONCAT(LT.TAG_TEXT SEPARATOR ', ')
FROM LK_TAG LT
INNER JOIN M_OBJECT_TAG_MAPPING MOTM
ON LT.PK_ID = MOTM.FK_TAG_ID
WHERE MOTM.FK_OBJECT_ID = MCI.FK_PROSPECT_ID
AND MOTM.OBJECT_TYPE = 1
AND MOTM.IS_ACTIVE = 1
) AS TAGS,
IFNULL(SUM(GET_DIGITS(MMR.RCP_AMOUNT)), 0) AS REVENUE_SO_FAR,
IFNULL(SUM(GET_DIGITS(MMR.RCP_RUPEES)), 0) AS REVENUE_INR,
COUNT(DISTINCT PMI_MONTHLY.PROJECT_ID) AS MONTHLY,
COUNT(DISTINCT PMI_FIXED.PROJECT_ID) AS FIXED,
COUNT(DISTINCT PMI_HOURLY.PROJECT_ID) AS HOURLY,
COUNT(DISTINCT PMI_ANNUAL.PROJECT_ID) AS ANNUAL,
COUNT(DISTINCT PMI_CURRENTLY_RUNNING.PROJECT_ID) AS CURRENTLY_RUNNING_PROJECTS,
COUNT(DISTINCT PMI_YET_TO_START.PROJECT_ID) AS YET_TO_START_PROJECTS,
COUNT(DISTINCT PMI_TECH_SALES_CLOSED.PROJECT_ID) AS TECH_SALES_CLOSED_PROJECTS
FROM
M_CLIENT_INFO MCI
INNER JOIN M_USER_DETAILS MUD_SP
ON MCI.ADDED_BY = MUD_SP.PK_ID
LEFT OUTER JOIN M_MONTH_RECEIPT MMR
ON MMR.CLIENT_ID = MCI.CLIENT_ID
LEFT OUTER JOIN M_PROJECT_INFO PMI_FIXED
ON PMI_FIXED.CLIENT_ID = MCI.CLIENT_ID AND PMI_FIXED.PROJECT_TYPE = 1
LEFT OUTER JOIN M_PROJECT_INFO PMI_MONTHLY
ON PMI_MONTHLY.CLIENT_ID = MCI.CLIENT_ID AND PMI_MONTHLY.PROJECT_TYPE = 2
LEFT OUTER JOIN M_PROJECT_INFO PMI_HOURLY
ON PMI_HOURLY.CLIENT_ID = MCI.CLIENT_ID AND PMI_HOURLY.PROJECT_TYPE = 3
LEFT OUTER JOIN M_PROJECT_INFO PMI_ANNUAL
ON PMI_ANNUAL.CLIENT_ID = MCI.CLIENT_ID AND PMI_ANNUAL.PROJECT_TYPE = 4
LEFT OUTER JOIN M_PROJECT_INFO PMI_CURRENTLY_RUNNING
ON PMI_CURRENTLY_RUNNING.CLIENT_ID = MCI.CLIENT_ID AND PMI_CURRENTLY_RUNNING.STATUS = 4
LEFT OUTER JOIN M_PROJECT_INFO PMI_YET_TO_START
ON PMI_YET_TO_START.CLIENT_ID = MCI.CLIENT_ID AND PMI_YET_TO_START.STATUS < 4
LEFT OUTER JOIN M_PROJECT_INFO PMI_TECH_SALES_CLOSED
ON PMI_TECH_SALES_CLOSED.CLIENT_ID = MCI.CLIENT_ID AND PMI_TECH_SALES_CLOSED.STATUS > 4
WHERE YEAR(MCI.DATE_ADDED) = '2012'
GROUP BY MCI.CLIENT_ID ORDER BY CLIENT_NAME ASC
Yes, as many people have said, the key is that when you have the where clause, mysql engine filters the table M_CLIENT_INFO --probably drammatically--.
A similar result as removing the where clause is to to add this where clause:
where 1 = 1
You will see that the performance is degraded also because mysql will try to get all the data.
Remove the where clause and all columns from select and add a count to see how many records you get. If it is reasonable, say up to 10k, then do the following,
put back the select columns related to M_CLIENT_INFO
do not include the nested one "TAGS"
remove all your joins
run your query without where clause and gradually include the joins
this way you'll find out when the timeout is caused.
I would try the following. First, MySQL has a keyword "STRAIGHT_JOIN" which tells the optimizer to do the query in the table order you've specified. Since all you left-joins are child-related (like a lookup table), you don't want MySQL to try and interpret one of those as a primary basis of the query.
SELECT STRAIGHT_JOIN ... rest of query.
Next, your M_PROJECT_INFO table, I dont know how many columns of data are out there, but you appear to be concentrating on just a few columns on your DISTINCT aggregates. I would make sure you have a covering index on these elements to help the query via an index on
( Client_ID, Project_Type, Status, Project_ID )
This way the engine can apply the criteria and get the distinct all out of the index instead of having to go back to the raw data pages for the query.
Third, your M_CLIENT_INFO table. Ensure that has an index on both your criteria, group by AND your Order By, and change your order by from the aliased "CLIENT_NAME" to the actual column of the SQL table so it matches the index
( Date_Added, Client_ID, Name )
I have "name" in ticks as it is also a reserved word and helps clarify the column, not the keyword.
Next, the WHERE clause. Whenever you apply a function to an indexed column name, it doesn't work the greatest, especially on date/time fields... You might want to change your where clause to
WHERE MCI.Date_Added between '2012-01-01' and '2012-12-31 23:59:59'
so the BETWEEN range is showing the entire year and the index can better be utilized.
Finally, if the above do not help, I would consider splitting your query some. The GROUP_CONCACT inline select for the TAGS might be a bit of a killer for you. You might want to have all the distinct elements first for the grouping per client, THEN get those details.... Something like
select
PQ.*,
group_concat(...) tags
from
( the entire primary part of the query ) as PQ
Left join yourGroupConcatTableBasis on key columns

COUNT evaluate to zero if no matching records

Take the following:
SELECT
Count(a.record_id) AS newrecruits
,a.studyrecord_id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
WHERE (((a.visit_type_id)=1))
GROUP BY a.studyrecord_id;
I want to amend the COUNT part to display a zero if there are no records since I assume COUNT will evaluate to Null.
I have tried the following but still get no results:
IIF(ISNULL(COUNT(a.record_id)),0,COUNT(a.record_id)) AS newrecruits
Is this an issue because the join is on record_id? I tried changing the INNER to LEFT but also received no results.
Q
How do I get the above to evaluate to zero if there are no records matching the criteria?
Edit:
To give a little detail to the reasoning.
The studies table contains a field called 'original_recruits' based on activity before use of the database.
The visits tables tracks new_recruits (Count of records for each study).
I combine these in another query (original_recruits + new_recruits)- If there have been no new recruits I still need to display the original_recruits so if there are no records I need it to evalulate to zero instead of null so the final sum still works.
It seems like you want to count records by StudyRecords.
If you need a count of zero when you have no records, you need to join to a table named StudyRecords.
Did you have one? Else this is a nonsense to ask for rows when you don't have rows!
Let's suppose the StudyRecords exists, then the query should look like something like this :
SELECT
Count(a.record_id) AS newrecruits -- a.record_id will be null if there is zero count for a studyrecord, else will contain the id
sr.Id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
LEFT OUTER JOIN studyrecord sr
ON sr.Id = a.studyrecord_id
WHERE a.visit_type_id = 1
GROUP BY sr.Id
I solved the problem by amending the final query where I display the result of combining the original and new recruits to include the IIF there.
SELECT
a.*
, IIF(IsNull([totalrecruits]),consents,totalrecruits)/a.target AS prog
, IIf(IsNull([totalrecruits]),consents,totalrecruits) AS trecruits
FROM
q_latest_studies AS a
LEFT JOIN q_totalrecruitment AS b
ON a.studyrecord_id=b.studyrecord_id
;

very complex SELECT

I want to do a complex SELECT between more tables (4+) that will order and count items.
So far this is what my line is :
SELECT
myl_u.id,
myl_u.label_real_address,
myl_u.ext,
COUNT(myc_c.contact_id),
COUNT(myl_r_c.release_id)
FROM
myl_users myl_u
LEFT JOIN myc_contacts myc_c ON myc_c.contact_type='l' AND myc_c.contact_id=myl_u.id
LEFT JOIN myl_releases myl_r ON myl_r.id=myl_u.id
LEFT JOIN myl_r_comments myl_r_c ON myl_r.release_id=myl_r_c.release_id
GROUP BY myl_u.label_real_address
ORDER BY COUNT(myc_c.contact_id) DESC
It's half working, but when I add the latter part of the SQL, it shows unexpected values and it doubles them too somehow.
Basically I have myl_users (a collection of record labels)
myc_contacts (how many favourites does a user have, contact_type='l' means it's about myl_users and not other table)
myl_releases contains music releases (like EP, album, with unique id's
and myl_r_comments contains comments regular users do to these releases.
I managed to ORDER by how many favourites a record label has (15, 14, 10, 8..) - the COUNT(myc_c.contact_id) clause
but when I add the next clause and make the query bigger to order by the total comments the releases from labels have, unexpected appears.
Can someone pinpoint me to the right way ?
I will comment and adapt / clarify the question by your needs.
thanks,
have a happy new year
The problem is that you are summing along multiple dimensions, so you are getting a cross product. The best way is to summarize along each dimension independently:
SELECT myl_u.id, myl_u.label_real_address, myl_u.ext,
sum(myc_c.cnt),
sum(myl_rc.cnt)
FROM myl_users myl_u LEFT JOIN
(select contact_id, count(*) as cnt
from myc_contacts myc_c
where myc_c.contact_type='l'
group by contact_id
) myc_c
ON myc_c.contact_id=myl_u.id LEFT JOIN
(select myl_r.id, count(*) as cnt
from myl_releases myl_r LEFT JOIN
myl_r_comments myl_r_c
ON myl_r.release_id=myl_r_c.release_id
gropu by myl_r.id
) myl_rc
ON myl_rc.id=myl_u.id
GROUP BY myl_u.id, myl_u.label_real_address, myl_u.ext
ORDER BY 4 DESC
It is not clear from the question whether the final group by is necessary. If there are no duplicates in the myl_u table, then you don't need the outside aggregation at all.
At least one problem that I spot is that you need a WHERE clause if you want to restrict the rows. The JOINs should only include the considtions of the JOINs.
SELECT
myl_u.id,
myl_u.label_real_address,
myl_u.ext,
COUNT(myc_c.contact_id),
COUNT(myl_r_c.release_id)
FROM
myl_users myl_u
LEFT JOIN myc_contacts myc_c ON myc_c.contact_id=myl_u.id
LEFT JOIN myl_releases myl_r ON myl_r.id=myl_u.id
LEFT JOIN myl_r_comments myl_r_c ON myl_r.release_id=myl_r_c.release_id
WHERE
myc_c.contact_types = '1'
GROUP BY myl_u.label_real_address
ORDER BY COUNT(myc_c.contact_id) DESC
Also, are you sure its a left join that you want? That returns all rows from the "left" table even if no matching values on the right. Try changing LEFT to INNER and see if you get what you are expecting.

Multiple GROUP_CONCAT on different fields using MySQL

I have a query like this:
SELECT product.id,
GROUP_CONCAT(image.id) AS images_id,
GROUP_CONCAT(image.title) AS images_title,
GROUP_CONCAT(facet.id) AS facets_id
...
GROUP BY product.id
And the query works, but not as expected, because if I have a product with 5 facets and 1 image (suppose an image with id=7), then I get something like this in "images_id":
"7,7,7,7,7"
If I have 2 images (7 and 3) then I get something like:
"7,7,7,7,7,3,3,3,3,3"
and in facets I get something like:
"8,7,6,5,4,8,7,6,5,4"
I think MySQL is making some type of union of the differents rows returned by the query, and then concatenating everything.
My expected result is (for the last example):
images_id = "7,3"
facets_id = "8,7,6,5,4"
I can obtain that using DISTINCT in the GROUP_CONCAT, but then I have another problem:
If I have two images with the same title, one of them is ommited, and then I get something like:
images_id = "7,3,5"
images_title = "Title7and3,Title5"
So I miss the relation between images_id and images_title.
Does somebody know if it's possible to make this query in MySQL?
Maybe I'm complicating everything without any real benefits.
I'm trying to execute only one query because performance, but now I'm not so sure if it's even faster to execute two queries (one for selecting the facets and another for the images for example).
Please explain what do you think is the best solution for this and why.
Thanks !
Just add DISTINCT.
Example:
GROUP_CONCAT(DISTINCT image.id) AS images_id
You'll need to get each group separately:
SELECT
p.id,
images_id,
images_title,
facets_id,
...
FROM PRODUCT p
JOIN (SELECT product.id, GROUP_CONCAT(image.id) AS images_id
FROM PRODUCT GROUP BY product.id) a on a.id = p.id
JOIN (SELECT product.id, GROUP_CONCAT(image.title) AS images_title
FROM PRODUCT GROUP BY product.id) b on b.id = p.id
JOIN (SELECT product.id, GROUP_CONCAT(facet.id) AS facets_id
FROM PRODUCT GROUP BY product.id) b on c.id = p.id
...
You can add just the DISTINCT keyword, you'll get your desire results.
SELECT tb_mod.*, tb_van.*,
GROUP_CONCAT(DISTINCT tb_voil.vt_id) AS voil,
GROUP_CONCAT(DISTINCT tb_other.oa_id) AS other,
GROUP_CONCAT(DISTINCT tb_ref.rp_id) AS referral
FROM cp_modules_record_tbl tb_mod
LEFT JOIN cp_vane_police_tbl tb_van ON tb_van.mr_id= tb_mod.id
LEFT JOIN cp_mod_voilt_tbl tb_voil ON tb_voil.mr_id= tb_mod.id
LEFT JOIN cp_mod_otheraction_tbl tb_other ON tb_other.mr_id= tb_mod.id
LEFT JOIN cp_mod_referral_tbl tb_ref ON tb_ref.mr_id= tb_mod.id
WHERE tb_mod.mod_type = 2 GROUP BY tb_mod.id
If the issue is speed, then it may be a lot faster to simply select all the data you need as separate rows, and do the grouping in the application, i.e.:
SELECT product.id, image.id, image.title, facet.id
Then in the application:
foreach row:
push product_id onto list_of_product_ids
push image_id onto list_of_image_ids
etc.