SQL Left Join Query to match all restriction from result - mysql

I´m trying to filter data with some categories where i get a result matching all given slug / type parameteres.
Currently i got stuck with extendending my current query to get the data with a single query. My current query looks like that:
SELECT t.slug, tc.t_id, tc.c_id
FROM tc
LEFT JOIN t ON t.id = tc.t_id
WHERE
(t.type = 'offerAges' AND t.slug = '14-16')
OR
(t.type = 'offerSportTypes' AND t.slug = 'football')
Gives me following result:
# slug t_id c_id
1 football 13 40
2 14-16 28 39
3 14-16 28 40
The result i want is that i just get the results where t_id AND c_id are matching. For example in my table the items 1 & 3.
Fiddle: https://www.db-fiddle.com/f/vQUU9C9iXj4gotE7YGDq6W/1

You can use aggregation. In this case:
SELECT tc.c_id
FROM tc JOIN
t
ON t.id = tc.t_id
WHERE (t.type = 'offerAges' AND t.slug = '14-16') OR
(t.type = 'offerSportTypes' AND t.slug = 'football')
GROUP BY tc.c_id
HAVING COUNT(DISTINCT t.type) = 2;
If you want the slugs, I would recommend using GROUP_CONCAT() to put them in a single column:
GROUP_CONCAT(t.slug)

Related

SQL error as a result of rewriting a query using subquery into a query using join

The original query:
SELECT o.offering_number,
o.english_description,
o.french_description,
fop.price_amount,
fop.price_type_code,
fop.price_status_code,
fop.offering_id,
(SELECT fop1.price_amount from facility_offering_price fop1
WHERE fop.offering_id = fop1.Offering_Id
AND fop1.price_type_code = 5
AND fop1.price_status_code = 3
) as 'priceAmount'
from facility_offering_price fop
join offering o on fop.offering_id = o.offering_id
WHERE fop.price_start_date = '15-10-28'
AND fop.price_status_code IN (1,2)
/*AND (price_status_code IS NULL)*/
AND fop.price_type_code = 5
/*AND (o.offering_number IS NULL)*/
ORDER BY o.offering_number ASC, fop.price_sequence_number ASC;
It produces a result of one entry.
The result query:
SELECT o.offering_number,
o.english_description,
o.french_description,
fop.price_amount,
fop2.price_amount,
fop.price_type_code,
fop.offering_id,
fop2.offering_id
from facility_offering_price fop
join offering o on fop.offering_id = o.offering_id
inner join
(select
fop1.offering_id,
fop1.price_amount
from facility_offering_price fop1
WHERE fop1.price_type_code = 5
AND fop1.price_status_code = 3
) fop2 on fop.offering_id = fop2.offering_id
WHERE fop.price_start_date = '15-10-28'
AND fop.price_status_code IN (1,2)
/*AND (price_status_code IS NULL)*/
AND fop.price_type_code = 5
/*AND (o.offering_number IS NULL)*/
ORDER BY o.offering_number ASC, fop.price_sequence_number ASC;
It's result set is empty. However, an entry is found if I ask for fop1.price_status_code = 1.
Unable to wrap my head around this one I would appreciate your help.
Try using LEFT JOIN instead. The conversion from SELECT a, subquery AS val FROM ... to a join is more accurately reflected that way. The original query would return rows with NULL val when the subquery has no results; your version ends up omitting such rows completely.

Counting entry in a column MySQL and display counts

I would want to count entries in a column and display the count beside it.
However, I'm clueless on how can I do it.
Desired output:
arrangement_number tray_no rl_type flag(count of occurrence)
------------------ ------- ---- ----
2774818 381001 R 3
2774818 381001 R 3
2774818 381001 L 3
2778470 405128 R 1
2779702 265265 R 2
2779702 265265 R 2
I'm currently trying queries using #variables but I still cant get it right.
each row are unique and I need them not to be grouped.
Update: Expanded Table added source code
Note: I'm currently joining 5 tables now
Actual Query:
SELECT
log.arrangement_number,
header.tray_number,
detail.rl_type,
-- some more fields here
FROM
log
INNER JOIN
header ON log.arrangement_number = header.rxarrangement_number
AND log.production_place_code = header.production_place_code
INNER JOIN
detail ON log.arrangement_number = detail.rxarrangement_number
AND log.production_place_code = detail.production_place_code
INNER JOIN
deliveryperiod ON log.arrangement_number = deliveryperiod.arrangement_number
AND log.production_place_code = deliveryperiod.production_place_code
AND detail.rl_type = deliveryperiod.rl_type
INNER JOIN
calc ON calc.arrangement_number = log.arrangement_number
AND calc.production_place_code = log.production_place_code
AND deliveryperiod.rl_type = calc.rl_type
AND detail.rl_type = calc.rl_type
WHERE
header.status_code IN ('20' , '21')
AND log.process_code = '12'
AND deliveryperiod.process_code_current = '12'
AND deliveryperiod.sub_process_code_current IN ('100' , '105')
AND lot_number = '120131'
ORDER BY log.lot_number , log.sequence_number , deliveryperiod.rl_type DESC
SELECT t1.tray_no,
t2.flag
FROM yourTable
INNER JOIN
(
SELECT tray_no, COUNT(*) AS flag
FROM yourTable
GROUP BY tray_no
) t2
ON t1.tray_no = t2.tray_no
try this...
SELECT tray_no, COUNT(*) 'flag'
FROM table1
GROUP BY tray_no

Count if a user has reached the borrwing limit

I've setup a fiddle with tables and data here
I'm trying to write a single sql to check if user has reached the borrowing limit for each category.
Right now, it's done using severals sql statements called after each other.
But the way it goes is simple.
memId and id come through a querystring.
$medId = $_POST['memId']; Using 1 for this example. This is the members Id.
$id = $_POST['id']; Using 4 for this example. This is the item being lent.
After that I do:
select id, holder from collection_db where id = 4 // We have a valid item
select borrowMax from collection_db where id = (holder from the previous select) and category = 10 //Result = 2. Category indicates its a label and not a borrowable item.
select count(borrowedId) from lendings where memId = 1 and holder = (holder from the 1st query) //He's borrowed 2, under 1, so cant borrow any more. User 2 may borrow however.
if (count => borrowMax) {echo 'Cannot borrow more.';} else {echo 'Added to'}
How can this be combined into a single sql or is it best left this way?
This seems to produce a correct result set:
SELECT col1.id, col1.holder, col2.borrowMax, count(lend.borrowedId) as `count`
FROM collection_db col1
INNER JOIN collection_db col2
ON col1.holder = col2.id
INNER JOIN lendings lend
ON col1.holder = lend.holder
WHERE col1.id = $id
AND col2.category = 10
AND lend.memId = $medId
I think this combines the queries:
select max(c.borrowMax) as BorrowMax, COUNT(*)
from collection_db c join
collection_db c1
on c.id = c1.holder and c1.id = 4 and c.category = 10 join
lendings l
on l.holder = c1.holder;
It does make an assumption that the join between c and c1 does not produce duplicate rows. But you have this requirement by using = in the original query (rather than join).

calculating multiple percentages in a single MySQL statement

I am new to complex SQL statements and need help.
I have managed to display half of the result set and struggling with remaining.
Result required:
PSU | pregnancy number| pregnancy percentage | % of live birth out of the pregnancy number.
1 | 2 | 67% | 40%
2 | 1 | 33% | 80%
With the sql written below , I have managed to get the first 3 columns.
The 2nd and the 3rd columns required where statement of fieldname = 'Q111'. While the 4th column requires percentage with where statement of fieldname = 'Q112.
How do I combine different where statements together.
SELECT sms_psu.name1 AS PSU,
woman_data.answertext AS NumberOfPreg,
Count(woman_data.answertext) / (SELECT Count(woman_data.answertext)
FROM woman_data
INNER JOIN sms_household
ON woman_data.prim_key =
ms_household.hhid
INNER JOIN sms_psu
ON sms_psu.psu =
sms_household.psu
WHERE sms_psu.state = 19
AND sms_psu.district = 1
AND sms_psu.psu = 2
AND fieldname = 'Q139') AS
totalcpuntpreg
FROM woman_data
INNER JOIN sms_household
ON woman_data.prim_key = sms_household.hhid
INNER JOIN sms_psu
ON sms_psu.psu = sms_household.psu
WHERE sms_psu.state = 19
AND sms_psu.district = 1
AND sms_psu.psu = 2
AND fieldname = 'Q111'
GROUP BY woman_data.answertext,
sms_household.psu
Gordon,Your suggestion helped me a lot.I updated my query accordingly.The issue that i am having write now is that iam getting an additional row for Q112
PSU | pregnancy number| pregnancy percentage | % of live birth out of the pregnancy number.
1 | 2 | 67% | 40%
2 | 1 | 33% | 80%
2 | 1 | 0% | 20% (3rd row is additional for Q112)
But I do not require the Q112 entry while i do group by. Can i use group by in case statements and in main where statement separately ? I tried using "having " with group by but dint get expected result.
Maybe something like this:
SELECT sms_psu.name1 AS PSU,
woman_data.answertext AS NumberOfPreg,
SUM(fieldname = 'Q111') / SUM(fieldname = 'Q139') AS pregnancyPercentage,
SUM(fieldname = 'Q112') / SUM(fieldname = 'Q139') AS liveBirthPercentage
FROM woman_data
INNER JOIN sms_household
ON woman_data.prim_key = sms_household.hhid
INNER JOIN sms_psu
ON sms_psu.psu = sms_household.psu
WHERE sms_psu.state = 19
AND sms_psu.district = 1
AND sms_psu.psu = 2
GROUP BY woman_data.answertext,
sms_household.psu
HAVING SUM(fieldname = 'Q111') > 0
The query first retrieves rows with all fieldname values and groups them same as before. When counting, only specific fieldname entries are counted (using the SUM(condition) trick) as necessary. To make sure that the output contains only groups that have rows with fieldname = 'Q111', the SUM(fieldname = 'Q111') > 0 condition was added (as a HAVING clause).
Your query is, as you mentioned, complex. It's hard for somebody unfamiliar with your schema to make sense of it. So I will offer a general suggestion for queries that summarize multiple items.
Use a decent client program that lets you save query text. If it also prettyprints (formats) your query text that is helpful.
Develop and test each summary query separately. Make sure each one, standing alone, produces the correct summary result you need from it.
Then, join those queries together on the reporting key (PSU in your case I believe) so it produces the multiple summary results. Make sure it is working.
Put a query name and version number in a comment right after the SELECT statement: for example,
SELECT /* pregnancy outcomes 1.1 */
sms_psu.name1 AS PSU,
woman_data.answertext AS NumberOfPreg, etc.
This will let you use the MySQL SHOW FULL PROCESSLIST to figure out which exact queries are running.
Finally, if the result query (consisting of lots of nested subqueries) is too slow for your application's requirements, optimize it.
If you follow these steps, your next step is to develop your % live birth summary query, I believe.
The answer to your question is: the CASE statement.
The idea is to extend the outer WHERE clause to include all rows you care about. Then, use a CASE statement in the SELECT clause to filter the rows, on a single variable basis.
I tried to put this together as:
SELECT sms_psu.name1 AS PSU,
woman_data.answertext AS NumberOfPreg,
Count(case when sms_psu.state = 19 AND sms_psu.district = 1 AND sms_psu.psu = 2 AND fieldname = 'Q111'
then woman_data.answertext
end) /
(SELECT Count(woman_data.answertext)
FROM woman_data INNER JOIN
sms_household
ON woman_data.prim_key = ms_household.hhid INNER JOIN
sms_psu
ON sms_psu.psu = sms_household.psu
WHERE sms_psu.state = 19 AND sms_psu.district = 1 AND sms_psu.psu = 2 AND
fieldname = 'Q139'
) AS totalcpuntpreg,
Count(case when sms_psu.state = 19 AND sms_psu.district = 1 AND sms_psu.psu = 2 AND fieldname = 'Q112'
then woman_data.answertext
end) /
(SELECT Count(woman_data.answertext)
FROM woman_data INNER JOIN
sms_household
ON woman_data.prim_key = ms_household.hhid INNER JOIN
sms_psu
ON sms_psu.psu = sms_household.psu
WHERE sms_psu.state = 19 AND sms_psu.district = 1 AND sms_psu.psu = 2 AND
fieldname = 'Q139'
) AS totalcpuntpreg
FROM woman_data INNER JOIN
sms_household
ON woman_data.prim_key = sms_household.hhid INNER JOIN
sms_psu
ON sms_psu.psu = sms_household.psu
WHERE sms_psu.state = 19 AND sms_psu.district = 1 AND sms_psu.psu = 2 AND
fieldname in ('Q111', 'Q112')
GROUP BY woman_data.answertext
sms_household.psu
However, I might have missed some details.

query is not working correctly

I have these tables:
single_user_has_university_has_course
single_user_users_id_user university_has_course_university_id_university university_has_course_course_id_course first_year_school last_year_school grade
1 1 1 2000 2001 15
And
university_has_course
university_id_university course_id_course
1 1 <<< I want to select this
15 1
1 3
15 3
The problem is that the query below return all rows where course_id_course = 1 but ignore theuniversity_id_university = 1`. Then, will return two rows.
query:
SELECT A.first_year_school, A.last_year_school, A.grade, U.university, C.course, T.type_course
FROM single_user_has_university_has_course A
INNER JOIN university_has_course Q
ON A.university_has_course_course_id_course = Q.course_id_course
INNER JOIN university U
ON Q.university_id_university = U.id_university
INNER JOIN course C
ON Q.course_id_course = C.id_course
INNER JOIN type_course T
ON C.type_course_id_type_course = T.id_type_course
AND A.single_user_users_id_user = ?
You are joining the last table to the previous joins on 2 constraints C.type_course_id_type_course = T.id_type_course AND A.single_user_users_id_user = ?. I think you want to write it as a WHERE clause. So replace your last line with:
WHERE A.single_user_users_id_user = ?
But you're not also trying to filter university_id_university = 1, so you may want to add it to the WHERE clause as well:
WHERE A.single_user_users_id_user = ? AND Q.university_id_university = ?