mysql grouping mre than one query - mysql

I try to explain the matter:
I have a hotel management system built with php+mysql.
I need to extract the number of:
current reservations visitors
arriving visitors
leaving visitors
and I need to know this numbers grouped by "region" (Region is either a country or a region of a country. I get this info from a stored function).
I have tried many times, but I always get values not in line with what I expect.
I have tried grouping by sum, by region, by the two of them, I have tried nesting the queries (horrible result...i'm not that good at subqueries).
Here are the 3 main queries:
Current visitors:
select p.num_persone, IFNULL(getcoderegione(c.regione),getNazioneCodeIstat(c.nazione)) regione, p.*, c.nazione, c.regione from prenota2015 p INNER JOIN clienti c ON c.idclienti = p.idclienti where p.iddatainizio < 94 and p.iddatafine >= 93 and p.idclienti != '0';
Arriving visitors:
select p.num_persone, IFNULL(getcoderegione(c.regione),getNazioneCodeIstat(c.nazione)) regione, p.*, c.nazione, c.regione from prenota2015 p INNER JOIN clienti c ON c.idclienti = p.idclienti where p.iddatainizio < 94 and p.iddatainizio >= 93 and p.idclienti != '0';
Leaving visitors:
select p.num_persone, IFNULL(getcoderegione(c.regione),getNazioneCodeIstat(c.nazione)) regione, p.*, c.nazione, c.regione from prenota2015 p INNER JOIN clienti c ON c.idclienti = p.idclienti where p.iddatafine < 93 and p.iddatafine >= 92 and p.idclienti != '0';
The IFNULL stuff checks if it should present the country code or the region code.
Table 'clienti' contains the value of the region or country for IFNULL check mentioned above and it is important for the grouping clause.
what I expect is something like:
| region code | visitors present | visitors arriving | visitors leaving
------------------------------------------------------------------------
| 219 | 3 | 1 | 1 |
------------------------------------------------------------------------
| 186 | 2 | 0 | 0 |
and so on.
Question time: I would you do it?
I tried last time with:
SELECT DISTINCT
IFNULL(getcoderegione(c.regione),getNazioneCodeIstat(c.nazione)) regione,
pres.presenze,
part.partenze,
arr.arrivi
FROM
(
select COUNT(p.idprenota) partenze, p.idclienti
from prenota2015 p
INNER JOIN clienti c ON c.idclienti = p.idclienti
where
p.iddatafine < 91
and p.iddatafine >= 90
GROUP BY regione
) as part,
(
select COUNT(p2.idprenota) arrivi, p2.idclienti
from prenota2015 p2
INNER JOIN clienti c ON c.idclienti = p2.idclienti
where
p2.iddatainizio < 91
and p2.iddatainizio >= 90
GROUP BY regione
) as arr,
(
select COUNT(p3.idprenota) presenze, p3.idclienti
from prenota2015 p3
INNER JOIN clienti c ON c.idclienti = p3.idclienti
where
p3.iddatainizio < 91
and p3.iddatafine >= 90
GROUP BY regione
) as pres,
clienti c
but it was a desperate attempt to try to understand how to move around it.
Any help? Suggestions?
Thank you very much

SELECT q1.regione,
q1.total AS current_visitors,
q2.total AS arriving_visitors,
q1.total AS leaving_visitors
FROM (
SELECT sum(p.num_persone) total,
IFNULL(getcoderegione(c.regione), getNazioneCodeIstat(c.nazione)) regione
FROM prenota2015 p
INNER JOIN clienti c ON c.idclienti = p.idclienti
WHERE p.iddatainizio < 94
AND p.iddatafine >= 93
AND p.idclienti != '0'
GROUP BY regione
) q1
INNER JOIN (
SELECT sum(p.num_persone) total,
IFNULL(getcoderegione(c.regione), getNazioneCodeIstat(c.nazione)) regione
FROM prenota2015 p
INNER JOIN clienti c ON c.idclienti = p.idclienti
WHERE p.iddatainizio < 94
AND p.iddatainizio >= 93
AND p.idclienti != '0'
GROUP BY regione
) q2 ON q1.regione = q2.regione
INNER JOIN (
SELECT sum(p.num_persone) total,
IFNULL(getcoderegione(c.regione), getNazioneCodeIstat(c.nazione)) regione
FROM prenota2015 p
INNER JOIN clienti c ON c.idclienti = p.idclienti
WHERE p.iddatafine < 93
AND p.iddatafine >= 92
AND p.idclienti != '0'
GROUP BY regione
) q3 ON q1.regione = q3.regione;
or, as moo suggested, create views of your three queries, which would make this one a lot neater

Related

Displaying data in two separate columns based on where clause in MYSQL

I have a system that has to display a students marks
How can I write my query such that it shows the exam mark and ca mark without showing the zeros, and also be able to properly group by subject ID, not duplicate the subject name
A subject must only appear once with the corresponding CA and Exam mark.
I tried group concat and distinct but no avail.
When I run this query
SELECT
DISTINCT marks.student_id,
GROUP_CONCAT(subjects.subject_name) AS subject_name,
subjects.id as sub_id,
(
CASE WHEN assessements.assessement_type = 1 THEN (
SELECT
ROUND(AVG(marks.mark)) as ca_average
FROM
marks
INNER JOIN assessements ON assessements.id = marks.assessement_id
INNER JOIN teaching_loads ON teaching_loads.id = marks.teaching_load_id
INNER JOIN subjects ON subjects.id = teaching_loads.subject_id
WHERE
marks.student_id = 576
AND assessements.assessement_type = 1
AND assessements.term_id = 2
AND subjects.id = sub_id
ORDER BY
subjects.id
) ELSE 0 END
) as ca,
(
CASE WHEN assessements.assessement_type = 2 THEN (
SELECT
marks.id
FROM
marks
INNER JOIN assessements ON assessements.id = marks.assessement_id
INNER JOIN teaching_loads ON teaching_loads.id = marks.teaching_load_id
INNER JOIN subjects ON subjects.id = teaching_loads.subject_id
WHERE
marks.student_id = 576
AND assessements.term_id = 2
AND assessement_type = 2
AND assessements.term_id = 2
AND subject_id = sub_id
ORDER BY
subjects.id
) ELSE 0 END
) as exam
FROM
marks
INNER JOIN assessements ON assessements.id = marks.assessement_id
INNER JOIN teaching_loads ON teaching_loads.id = marks.teaching_load_id
INNER JOIN subjects ON subjects.id = teaching_loads.subject_id
WHERE
marks.student_id = 576
GROUP BY
subjects.id,
assessements.assessement_type
This is what the query produces
How can I write my query such that it shows the exam mark and ca mark without showing the zeros, be able to properly group by subject ID,
Something like this
**Student ID | Subject Name | sub_id | ca | exam**
576 English 2 40 54
576 Geography 34 30 34
Don't use sub-queries, use conditional aggregation.
SELECT
marks.student_id,
GROUP_CONCAT(subjects.subject_name) AS subject_name,
subjects.id as sub_id,
ROUND(
AVG(
CASE WHEN assessements.assessement_type = 1
AND assessements.term_id = 2
THEN marks.mark
END
)
)
AS ca,
ROUND(
AVG(
CASE WHEN assessements.assessement_type = 2
AND assessements.term_id = 2
THEN marks.mark
END
)
) as exam
FROM
marks
INNER JOIN assessements ON assessements.id = marks.assessement_id
INNER JOIN teaching_loads ON teaching_loads.id = marks.teaching_load_id
INNER JOIN subjects ON subjects.id = teaching_loads.subject_id
WHERE
marks.student_id = 576
GROUP BY
marks.student_id,
subjects.id
CASE expressions that don't have an ELSE implicitly return NULL is none of the WHEN clauses return TRUE.
Also AVG() and other aggregates essentially ignore NULL values. This means that the average of NULL, 1, 2, 3 is 2.
Using these together, the above code returns the average of marks.marks for assessement_type = 1 in one columns, and the average of marks.marks for assessement_type = 2 in a separate column.

query data (rows) on another table with same id and removing the result with only one data

Here I have example of my SELECT query returning data but I want to remove or exclude the result with only 1 data return.
SELECT m.roverbook,
m.Sequence,
m.AccountNumber,
m.Name,
c.duedate,
c.amount as Bill
FROM unpaidbills c
LEFT JOIN master m ON (m.id = c.uid)
WHERE m.roverbook = '010101'
AND c.duedate < '2017-01-10'
ORDER BY m.Sequence
RESULT SAMPLE
roverbook Sequence AccountNumber Name duedate Bill
10101 1001 1010010 CHUA, SOLIDAD 1/5/2003 514.41
10101 1001 1010010 CHUA, SOLIDAD 2/5/2003 614.97
10101 1001 1010010 CHUA, SOLIDAD 6/5/2003 264.58
10101 1002 1010040 CRUZ, BENITO 10/5/2001 1156.26
10101 1002 1010040 CRUZ, BENITO 11/5/2001 1775.42
10101 1003 1010051 SERRANO, NOMER 10/5/2001 633.41
10101 1004 1010221 C H A P E L 11/7/2014 128.41
I want to remove the result with only one Bill
SELECT m.roverbook,
m.Sequence,
m.AccountNumber,
m.Name,
c.duedate,
c.amount as Bill
FROM unpaidbills c
LEFT JOIN master m ON (m.id = c.uid)
Left join
(
SELECT
m.id,
count(c.uid) as BillCount
FROM unpaidbills c
LEFT JOIN master m ON (m.id = c.uid)
WHERE m.roverbook = '010101'
AND c.duedate < '2017-01-10'
group by m.id
having count(c.uid)>1
) MorethanoneBill
on(m.id = MorethanoneBill.id)
WHERE m.roverbook = '010101'
AND c.duedate < '2017-01-10'
ORDER BY m.Sequence
Try this it will return records with multiple bills only based on sequence:
SELECT m.roverbook,
m.Sequence,
m.AccountNumber,
m.Name,
c.duedate,
c.amount
FROM unpaidbills c
LEFT JOIN (SELECT * FROM master WHERE sequence IN (select m1.sequence from master m1 group by m1.roverbook, m1.sequence having count(m1.sequence) > 1)) m ON (m.id = c.uid)
WHERE m.roverbook = '010101'
AND c.duedate < '2017-01-10'
ORDER BY m.Sequence

How can I optimize this mysql query having left joins as columns?

This query is taking over 4 seconds to run killing the performance on the page.
The query is doing a sum of points getting the max point by day for a product for a particular month. The purpose is to display a ranking for a given month ordered by the sum of points.
SELECT eriginal_asin, DATE_FORMAT(date_time,'%m/%d/%Y %h:%i:%s') as date_time, SUM(maxpoints) as points, `o1`.`value` as value,
`o2`.`value` as value1, `o3`.`value` as value2, `o4`.`value` as value3, `o5`.`value` as value4, `o6`.`value` as value5,
`o7`.`value` as value6, `o8`.`value` as value7, `o9`.`value` as value8, `o10`.`value` as value9, `o11`.`value` as value10,
`o12`.`value` as value11, `o13`.`value` as value12, `o14`.`value` as value13, `o15`.`value` as value14, `o16`.`value` as value15,
`o17`.`value` as value16, property.id, user.name_surname, user.name_last
from
(SELECT id, eriginal_asin, max(points) as maxpoints, DATE_FORMAT(date_time,'%m/%d/%Y %h:%i:%s') as date_time
from ranking
where date_time >= '2015-09-01'
and date_time <= '2015-09-30 23:59:59'
and points > 0
group by eriginal_asin, region, date(date_time)
)rankmax, property
LEFT JOIN `property_user` ON `property`.`id` = `property_user`.`property_id`
LEFT JOIN `user` ON `property_user`.`user_id` = `user`.`id`
LEFT JOIN `property_value` o1 ON `property`.`id` = `o1`.`property_id` and o1.option_id = 17
LEFT JOIN `property_value` o2 ON `property`.`id` = `o2`.`property_id` and o2.option_id = 10
LEFT JOIN `property_value` o3 ON `property`.`id` = `o3`.`property_id` and o3.option_id = 54
LEFT JOIN `property_value` o4 ON `property`.`id` = `o4`.`property_id` and o4.option_id = 64
LEFT JOIN `property_value` o5 ON `property`.`id` = `o5`.`property_id` and o5.option_id = 65
LEFT JOIN `property_value` o6 ON `property`.`id` = `o6`.`property_id` and o6.option_id = 5
LEFT JOIN `property_value` o7 ON `property`.`id` = `o7`.`property_id` and o7.option_id = 6
LEFT JOIN `property_value` o8 ON `property`.`id` = `o8`.`property_id` and o8.option_id = 10
LEFT JOIN `property_value` o9 ON `property`.`id` = `o9`.`property_id` and o9.option_id = 63
LEFT JOIN `property_value` o10 ON `property`.`id` = `o10`.`property_id` and o10.option_id = 55
LEFT JOIN `property_value` o11 ON `property`.`id` = `o11`.`property_id` and o11.option_id = 56
LEFT JOIN `property_value` o12 ON `property`.`id` = `o12`.`property_id` and o12.option_id = 57
LEFT JOIN `property_value` o13 ON `property`.`id` = `o13`.`property_id` and o13.option_id = 58
LEFT JOIN `property_value` o14 ON `property`.`id` = `o14`.`property_id` and o14.option_id = 59
LEFT JOIN `property_value` o15 ON `property`.`id` = `o15`.`property_id` and o15.option_id = 60
LEFT JOIN `property_value` o16 ON `property`.`id` = `o16`.`property_id` and o16.option_id = 61
LEFT JOIN `property_value` o17 ON `property`.`id` = `o17`.`property_id` and o17.option_id = 62
where property.is_activated = 1
and o1.value = eriginal_asin
GROUP BY DATE(date_time), eriginal_asin
order by points desc
one thing is, you can do it with one LEFT JOIN. I have made a sample (not with all columns) to test if it works better
SELECT eriginal_asin,
DATE_FORMAT(date_time,'%m/%d/%Y %h:%i:%s') AS date_time,
SUM(maxpoints) AS points,
coalesce( IF( o.property_id = 17, `o`.`value`, NULL )) AS value_1,
coalesce(IF (o.property_id = 10, `o`.`value`, NULL )) AS value_
coalesce(IF (o.property_id = 54, `o`.`value`, NULL )) AS value_3,
coalesce(IF (o.property_id = 64, `o`.`value`, NULL )) AS value_4,
property.id,
user.name_surname,
user.name_last
FROM
(SELECT id,
eriginal_asin,
max(points) AS maxpoints,
DATE_FORMAT(date_time,'%m/%d/%Y %h:%i:%s') AS date_time
FROM ranking
WHERE date_time >= '2015-09-01'
AND date_time <= '2015-09-30 23:59:59'
AND points > 0
GROUP BY eriginal_asin,
region,
date(date_time) )rankmax,
property
LEFT JOIN `property_user` ON `property`.`id` = `property_user`.`property_id`
LEFT JOIN `user` ON `property_user`.`user_id` = `user`.`id`
LEFT JOIN `property_value` o ON `property`.`id` = `o`.`property_id`
WHERE property.is_activated = 1
AND o1.value = eriginal_asin
GROUP BY o.property_id,
DATE(date_time),
eriginal_asin
ORDER BY points DESC;
A few things... Can you please provide some sample data of your "Property_Value" table records... what are the "values". Ranking.ID... Is that the ID associated to the property? You have no join condition to that and THAT may be causing a Cartesian result.
Does the Ranking table have a "Property_ID" column to join against. Do you have index on your property_value table on ( value, id, option_id )... A combination/multi-field index would significantly help too.
Also, it is unclear exactly how / where the property ID is based on the RankMax table -- or does the property table have the "eriginal_asin" column (doubt it).
Existing table structures and some sample data (space formatting of data instead of tabs preferred) would help myself and/or others to helping you out.
EXAMPLE ONLY OF DATA (you can copy/paste in your original question and adjust content as needed), but your join criteria really is missing somewhere.
Property Table
ID Is_Activiated OtherDescript
1 1 Test Sample
2 0 Another Property
3 1 last
User Table
ID UserName
PropertyUser Table
ID Property_ID
1 3
2 1
3 2
Property_value table
ID Property_ID Option_ID Value
1 3 17 blah
1 3 10 another
1 3 54 blah 2

MySQL join 3 calculation results

I have 3 different MySQL calculations, which I'd like to join. I need to be able to show lines where a sum may not exist for some column, or some invoice might not have a corporation ID.
I'm trying to get something like:
passport_amount | invoice_amount | balance_amount | corporation_id
------------------------------------------------------------------
345 | 2345 | 56 | 56
So that I can work on these values in my application code by iterating the list once and not fetching data from the database three times, and then iterating three times to combine the values.
SELECT
sum(passports.amount) AS passports_amount,
companies.corporation_id
FROM
passports
INNER JOIN
employees ON ( passports.employee_id = employees.id )
INNER JOIN
companies ON ( employees.company_id = companies.id )
WHERE
((((passports.pass_type IN ('sport','culture','both'))
AND
(MONTH(passports.valid_from) >= 1
AND MONTH(passports.valid_from) <= 9
AND YEAR(passports.valid_from) = year(now())))
AND (passports.removed = 0
AND passports.valid_from <= date('2014-09-29 11:55:26')))
AND (companies.removed = 0)
AND companies.corporation_id IS NOT NULL)
GROUP BY
companies.corporation_id;
SELECT
sum(invoices.amount) AS invoices_amount,
invoices.corporation_id
FROM
invoices
WHERE
((((YEAR(sent_at) = 2014)
AND (invoices.product_type_id IN (2,3,4)))
AND
(invoices.removed = 0
AND invoices.activated = 1))
AND invoices.corporation_id IS NOT NULL)
GROUP BY
invoices.corporation_id;
SELECT
amount AS balance_amount,
business_id AS corporation_id
FROM
invoice_balances
WHERE
business_type = 'Corporation';
You can combine queries for balance and invoice amount using sub select in left join but this will look odd and can be expensive in terms of performance
SELECT
SUM(p.amount) AS passports_amount,
ii.invoices_amount,
b.balance_amount,
c.corporation_id
FROM
passports p
INNER JOIN employees e ON ( p.e = e.id )
INNER JOIN companies c ON ( e.company_id = c.id )
/* Added left join for balance using subselect*/
LEFT JOIN (
SELECT amount AS balance_amount, business_id AS corporation_id
FROM invoice_balances
WHERE business_type = 'Corporation'
) b ON (c.corporation_id = b.corporation_id)
/* Added left join for invoices_amount using subselect*/
LEFT JOIN(
SELECT SUM(i.amount) AS invoices_amount,
i.corporation_id
FROM
invoices i
WHERE
((((YEAR(sent_at) = 2014)
AND (i.product_type_id IN (2,3,4)))
AND (i.removed = 0 AND i.activated = 1)
)
AND i.corporation_id IS NOT NULL)
GROUP BY i.corporation_id
) ii ON(c.corporation_id = ii.corporation_id)
/* end of joins */
WHERE
((((p.pass_type IN ('sport','culture','both'))
AND
(MONTH(p.valid_from) >= 1
AND MONTH(p.valid_from) <= 9
AND YEAR(p.valid_from) = YEAR(NOW())))
AND (p.removed = 0
AND p.valid_from <= DATE('2014-09-29 11:55:26')))
AND (c.removed = 0)
AND c.corporation_id IS NOT NULL)
GROUP BY c.corporation_id;

MySQL select two columns multiple name value pair

I need help about generating query for multiple column.
part of my tbl_advert_specific_fields_values table look like:
id advert_id field_name field_value
1 654 t1_sqft 50
2 655 t1_yearbuilt 1999
3 1521 t2_doorcount 5
4 656 t1_yearbuilt 2001
5 656 t1_sqft 29
6 654 t1_yearbuilt 2004
SELECT p.*, p.id AS id, p.title AS title, usr.id as advert_user_id,
p.street_num, p.street,c.icon AS cat_icon,c.title AS cat_title,c.title AS cat_title,
p.description as description,
countries.title as country_name,
states.title as state_name,
date_FORMAT(p.created, '%Y-%m-%d') as fcreated
FROM tbl AS p
LEFT JOIN tbl_advertmid AS pm ON pm.advert_id = p.id
INNER JOIN tbl_usermid AS am ON am.advert_id = p.id
LEFT JOIN tbl_users AS usr ON usr.id = am.user_id
INNER JOIN tbl_categories AS c ON c.id = pm.cat_id
INNER JOIN tbl_advert_specific_fields_values AS asfv ON asfv.advert_id = p.id
LEFT JOIN tbl_countries AS countries ON countries.id = p.country
LEFT JOIN tbl_states AS states ON states.id = p.locstate
WHERE p.published = 1 AND p.approved = 1 AND c.published = 1
AND (asfv.field_name = 't1_yearbuilt'
AND CONVERT(asfv.field_value,SIGNED) <= 2004 )
AND (asfv.field_name = 't1_sqft'
AND CONVERT(asfv.field_value,SIGNED) <= 50)
AND p.price <= 10174945 AND (p.advert_type_id = 1)
AND (c.id = 43 OR c.parent = 43)
GROUP BY p.id
ORDER BY p.price DESC
ok, the problem is in this asfv query part that are generated dynamically. It belong to objects which represent adverts by its specific fields. asfv is actually advert_specific_fields_values table (table name say all about it).
Without part:
AND (asfv.field_name = 't1_yearbuilt'
AND CONVERT(asfv.field_value,SIGNED) <= 2004 )
AND (asfv.field_name = 't1_sqft'
AND CONVERT(asfv.field_value,SIGNED) <= 50)
query return all adverts that belong on advert_type_id and price of them are less than 10.174.945,00 €.
All what I need is query update that return only adverts, for example t1_yearbuilt less than 2005 and t1_sqft less than 51 (advert_id => 654,656).
I also need query for values between for example t1_sqft >=30 AND t1_sqft <=50 (advert_id => 654).
Can anybody know how, update this query?
TNX