SQL Advice - selecting multiple rows from a table as an innerJoin - php

I've been working on a project that until now has only needed to find 1 row from the joined table. But now I need to grab multiple rows..
So as it stand my sql works something like:
Select rows for each company for this particular project which alone would find company details (name, id, telephone.. blah).
Then I join a table that contains form data submitted for each company (multiple forms - so multiple records)
Until now i have been specifying one formid to look for in the join, but now i need to specify multiple ones.
If I use WHERE form_id = 1 OR form_id = 2 OR form_id = 3 ... I get a result of only the first form match that is found per company..
If I mix up the query so it looks for the forms 1st and returns multiple records for each company with different form data - that works in this sense..
But I am then looping through this array in a view and creating a table row per record (previously each row was a new company) but using the latter would cause multiple records to show for the same company.
Any way I can do this? I tried group by with the latter method but this results in only 1 record again.
SELECT DISTINCT p.project_company_has_user_id, p.project_company_has_user_project_id, p.project_company_has_user_user_id, c.company_id, c.company_hall_no, c.company_company_name, c.company_type, c.company_country, c.company_stand_number, c.company_image_file_1, p2.project_id, p2.project_name, u.user_id, u.user_username, o.orders_id, o2.order_detail_id, o2.order_detail_product_id, f2.form_question_has_answer_id, f2.form_question_has_answer_request, f2.form_question_has_answer_form_id, f2.form_question_has_answer_user_id
FROM project_company_has_user p
INNER JOIN company c ON p.project_company_has_user_company_id = c.company_id
INNER JOIN project p2 ON p.project_company_has_user_project_id = p2.project_id
INNER JOIN user u ON p.project_company_has_user_user_id = u.user_id
INNER JOIN form f ON p.project_company_has_user_project_id = f.form_project_id
LEFT JOIN orders o ON p.project_company_has_user_user_id = o.orders_user_id
LEFT JOIN order_detail o2 ON ((o2.order_detail_orders_id = o.orders_id AND (o2.order_detail_product_id = 65 OR o2.order_detail_product_id = 68 OR o2.order_detail_product_id = 64)))
LEFT JOIN form_question_has_answer f2 ON ((f2.form_question_has_answer_form_id = 297 AND f2.form_question_has_answer_user_id = p.project_company_has_user_user_id))
WHERE (f.form_template_name = "custom" AND p.project_company_has_user_garbage_collection = 0 AND p.project_company_has_user_project_id = 48) AND (LCASE(c.company_country) LIKE "%uk%" OR LCASE(c.company_country) LIKE "%uk%") ORDER BY company_company_name asc

you need another field in order_detail as o2 . this field is row_index(position),etc for positioning record
LEFT JOIN order_detail o2 ON (o2.row_index=1 AND (o2.order_detail_orders_id = o.orders_id AND (o2.order_detail_product_id = 65 OR o2.order_detail_product_id = 68 OR o2.order_detail_product_id = 64)))

Personally I would use an Outer Join for the table of which elements you need to list all matches. Should you them need to clean up that data you can build the logic into the Join Condition (as step 2). Depending on the volume of data you are handling and whether or not you need to reuse it later in the same proc, you may want to post that primary dataset into a temp table and use that as source (primary) for your later logic.
Hope that helps. If you need the code, let me know, but it is pretty straight forward.
Regards
Mac

Related

MYSQL PHP select the opposite of a query

I have two tables.
One table has everything I need including a cardId(PK)
The other name is a user type table. This table stores the userId and the cardId(FK).
SELECT ci.cardId, ci.year, ci.name, ci.number
FROM USERCARDS uc
INNER JOIN CARDINDEX ci ON uc.cardId = ci.cardId
WHERE uc.userId = 'USER_ID'
So for this query, it will display the cardId, year, name, and number from the CARDINDEX. It will only display the cards that the user has saved in USERCARDS.
I want to do the opposite. If a user is looking at, lets just say, 5 cards, but the CARDINDEX has 50 cards, this query will display the information for the five cards. However, for a new query, I would want to show the remaining 45 cards. Basically, they cant add a card they already are following.
I tried to have uc.cardId != ci.cardId but that didn't work. Im kind of lost.
You can phrase this as a LEFT JOIN:
SELECT ci.cardId, ci.year, ci.name, ci.number
FROM CARDINDEX ci LEFT JOIN
USERCARDS uc
ON uc.cardId = ci.cardId AND
uc.userId = 'USER_ID'
WHERE uc.cardID IS NULL;
Alternatively, you could write this using `NOT EXISTS:
SELECT ci.cardId, ci.year, ci.name, ci.number
FROM CARDINDEX ci
WHERE NOT EXISTS (SELECT 1
FROM USERCARDS uc
WHERE uc.cardId = ci.cardId AND
uc.userId = 'USER_ID'
);

SQL - select column inside different table if not null

I am trying to make a "recipe" system inside a game. The player can own a company and craft items in there.
I currently fetch the recipes per company type but I don't know how to write the query in a way that I can also fetch the item names and images if the item_id is not empty.
This is working:
SELECT a.recipe_id,
a.item1_id,
a.item1_uses,
a.item2_id,
a.item2_uses,
a.item3_id,
a.item3_uses,
a.item4_id,
a.item4_uses,
a.item5_id,
a.item5_uses,
a.newitem_id,
a.newitem_uses,
a.craft_description,
a.craft_button
FROM
company_recipes AS a,
company_types AS b
WHERE
a.type_id = b.type_id
AND
b.type_id = '".$type."';
"
A recipe can contain for example two items needed to craft something new, but it could also be 5. So if it's only 2, I only want to fetch the img, name of these 2 and the rest can be skipped.
I have a different table store_items that contains the img and name of the item. I was thinking something along the lines of an IF ELSE or CASE WHEN inside the query, but I'm not sure how I'd do that.
Something like: SELECT c.img, c.name FROM store_items AS c IF a.item1_id is not NULL.
I feel like I'm close to the solution, but missing the last step.
Thanks for the tips #jarlh, I've changed the code and came to this result. If you have any more tips to do it better I'm happy to listen. (I'm still a junior and thought myself by trial and error, so I might not have the best solutions at times... Which is why tips are highly appreciated).
SELECT cr.recipe_id,
cr.item1_id,
cr.item1_uses,
si1.name,
si1.img,
cr.item2_id,
cr.item2_uses,
si2.name,
si2.img,
cr.item3_id,
cr.item3_uses,
si3.name,
si3.img,
cr.item4_id,
cr.item4_uses,
si4.name,
si4.img,
cr.item5_id,
cr.item5_uses,
si5.name,
si5.img,
cr.newitem_id,
cr.newitem_uses,
si_new.name,
si_new.img,
cr.craft_description,
cr.craft_button
FROM
company_recipes AS cr
INNER JOIN company_types AS ct ON cr.type_id = ct.type_id
LEFT JOIN store_items AS si1 ON cr.item1_id = si1.item_id
LEFT JOIN store_items AS si2 ON cr.item2_id = si2.item_id
LEFT JOIN store_items AS si3 ON cr.item3_id = si3.item_id
LEFT JOIN store_items AS si4 ON cr.item4_id = si4.item_id
LEFT JOIN store_items AS si5 ON cr.item5_id = si5.item_id
LEFT JOIN store_items AS si_new ON cr.newitem_id = si_new.item_id
WHERE
ct.type_id = '".$type."';
I'm basically fetching everything now and handle the NULLs in the php code now.
Without seeing more info its had to see what you are trying achieve but you could start by using the the users inpute of the game to determine what data is first required before futher filtering. Try this:
Declare #Value int
set #Value = #User_input --- uses what ever the game user will
SELECT
a.recipe_id,
a.item1_id,
a.item1_uses,
a.item2_id,
a.item2_uses,
a.item3_id,
a.item3_uses,
a.item4_id,
a.item4_uses,
a.item5_id,
a.item5_uses,
a.newitem_id,
a.newitem_uses,
a.craft_description,
a.craft_button
--- you can insert more columns but i stopped here as i dont know what data you have in the other tables.
FROM
company_recipes a
INNER JOIN company_types b ON a.type_id = b.type_id
INNER JOIN store_items c ON c.type_id = b.type_id
WHERE
b.type_id = #Value; --- '".$type."';

Mysql Complex Join Query Issue

I have 5 mysql tables as described below.
clinics table
id
name
d_location_subscription table
id
clinic_id
t_id //t_id will contain a foreign key of d_cities, d_states or d_countries table
type "country" "state" "city"
d_countries table
id
name
code
d_states table
id
d_country_id
name
code
d_city table
id
d_state_id
name
code
d_location_subscription table is used to record clinic's subscription for a location(it may be a city, state or country). I'm expecting to get all subscribed cities for a specific
clinic using d_location_subscription table.
For example, if clinic A is subscribed to Texas state, I should be able to get all city ids for clinic A.
I created following sql query, it looks ugly but generate a close result what i want to achieve.
select
`d`.`id` AS `clinic_id`,
if((`dct`.`id` is not null),`dct`.`id`,if((`dct1`.`id` is not null),`dct1`.`id`,`dct2`.`id`)) AS `d_city_id`
from ((((((((
`d_location_subscriptions` `dls`
join `clinics` `d`
on((`d`.`id` = `dls`.`clinic_id`)))
left join `d_countries` `dc`
on(((`dc`.`id` = `dls`.`t_id`) and (`dls`.`type` = 'country'))))
left join `d_states` `ds`
on((`ds`.`d_country_id` = `dc`.`id`)))
left join `d_cities` `dct2`
on((`dct2`.`d_state_id` = `ds`.`id`)))
left join `d_states` `ds1`
on(((`ds1`.`id` = `dls`.`t_id`) and (`dls`.`type` = 'state'))))
left join `d_cities` `dct`
on((`dct`.`d_state_id` = `ds1`.`id`)))
left join `d_cities` `dct1`
on(((`dct1`.`id` = `dls`.`t_id`) and (`dls`.`type` = 'city'))))
)
when there is record with type "country" in d_location_subscription table, I receive following result. total number of records returned are equal to the number of d_states table records.
How should I get rid of those Null values by changing above query?
And please advice me if this is the correct way to acheive similar functionality. Thanks in advance :)
The quickest, dirtiest way to achieve what you want is just to append this where condition to your query:
WHERE d_city_id is not null
but you might prefer to rework your query and decide where you really need LEFT joins and not INNER joins
the IF() computed column is in essence what STT LCU was trying to offer, but you can't use that directly in the where for some reason.
I've rewritten your query, but with different aliases to better follow the origination of the tables / relationships to get the data. In the end, I've added a where to test for ANY ONE of the "ID" values as NOT NULL. If they are ALL Null, the record should be excluded..
select
d.id AS clinic_id,
if(CityViaState.id is not null, CityViaState.id,
if( ByCity.id is not null, ByCity.id, CityViaCountry.id )) AS d_city_id
from
d_location_subscriptions dls
join clinics d
ON dls.clinic_id = d.id
left join d_countries ByCountry
ON dls.t_id = ByCountry.id
and dls.type = 'country'
left join d_states StateViaCountry
ON ByCountry.id = StateViaCountry.d_country_id
left join d_cities CityViaCountry
ON StateViaCountry.id = CityViaCountry.d_state_id
left join d_states ByState
ON dls.t_id = ByState.id
and dls.type = 'state'
left join d_cities CityViaState
ON ByState.id = CityViaState.d_state_id
left join d_cities ByCity
ON dls.t_id = ByCity.id
and dls.type = 'city'
where
CityViaState.id is not null
OR ByCity.id is not null
OR CityViaCountry.id is not null

Joining Multiple Rows From Within

The Problem
I have four tables I need to pull info from, two are just simple joins.
My query is returning nothing. Basically, there are forms I need to make sure I tied to my patient from one table, and the next table, to allow me to pull the form name so it's the correct form.
What I have
select person.first, person.last, treatmentPlan.created, treatmentPlan.updated, treatmentPlan.provider_id, intake.created, intake.updated, assesment.created, assesment.updated, discharge.created, discharge.updated
from form_patient_tie intake
LEFT OUTER JOIN form_patient_tie treatmentPlan ON intake.patient_id = treatmentPlan.patient_id
LEFT OUTER JOIN form_patient_tie assesment ON intake.patient_id = assesment.patient_id
LEFT OUTER JOIN form_patient_tie discharge ON intake.patient_id = discharge.patient_id
JOIN form as intakeForm ON intakeForm.form_id = intake.form_id
LEFT OUTER JOIN form treatmentPlanForm ON treatmentPlan.form_id = treatmentPlanForm.form_id
LEFT OUTER JOIN form assesmentForm on assesment.form_id = assesmentForm.form_id
LEFT OUTER JOIN form dischargeForm on discharge.form_id = dischargeForm.form_id
JOIN patient ON intake.patient_id = patient.patient_id
JOIN person on patient.person_id = person.person_id
WHERE (treatmentPlanForm.form_name LIKE "%Counseling Treatment Plan%" OR treatmentPlanForm.form_name IS NULL)
AND (intakeForm.form_name LIKE "%Counseling Intake%" OR intakeForm.form_name IS NULL)
AND (assesmentForm.form_name LIKE "%Counseling Assesment Review%" OR assesmentForm.form_name IS NULL)
AND (dischargeForm.form_name LIKE "%Counseling Discharge%" OR dischargeForm.form_name IS NULL)
It returns nothing even though I know there is a patient with Intake, Review, and Assessment done, but no discharge. If they have all 4, it works right, but if they are missing one, it doesn't.
It should look like
John Doe 2013-01-13 2013-01-15 2013-02-13 etc. It's definitely allowed for them to be null as not every person will have one filled out.
It looks to me as though your join to the "form" table (intakeForm alias) should also be a LEFT JOIN. Otherwise if there is not a match on the intake form, the entire query will return nothing.

MySQL Join and create new column value

I have an instrument list and teachers instrument list.
I would like to get a full instrument list with id and name.
Then check the teachers_instrument table for their instruments and if a specific teacher has the instrument add NULL or 1 value in a new column.
I can then take this to loop over some instrument checkboxes in Codeigniter, it just seems to make more sense to pull the data as I need it from the DB but am struggling to write the query.
teaching_instrument_list
- id
- instrument_name
teachers_instruments
- id
- teacher_id
- teacher_instrument_id
SELECT
a.instrument,
a.id
FROM
teaching_instrument_list a
LEFT JOIN
(
SELECT teachers_instruments.teacher_instrument_id
FROM teachers_instruments
WHERE teacher_id = 170
) b ON a.id = b.teacher_instrument_id
my query would look like this:
instrument name id value
--------------- -- -----
woodwinds 1 if the teacher has this instrument, set 1
brass 2 0
strings 3 1
One possible approach:
SELECT i.instrument_name, COUNT(ti.teacher_id) AS used_by
FROM teaching_instrument_list AS i
LEFT JOIN teachers_instruments AS ti
ON ti.teacher_instrument_id = i.id
GROUP BY ti.teacher_instrument_id
ORDER BY i.id;
Here's SQL Fiddle (tables' naming is a bit different).
Explanation: with LEFT JOIN on instrument_id we'll get as many teacher_id values for each instrument as teachers using it are - or just a single NULL value, if none uses it. The next step is to use GROUP BY and COUNT() to, well, group the result set by instruments and count their users (excluding NULL-valued rows).
If what you want is to show all the instruments and some flag showing whether or now a teacher uses it, you need another LEFT JOIN:
SELECT i.instrument_name, NOT ISNULL(teacher_id) AS in_use
FROM teaching_instrument_list AS i
LEFT JOIN teachers_instruments AS ti
ON ti.teacher_instrument_id = i.id
AND ti.teacher_id = :teacher_id;
Demo.
Well this can be achieved like this
SELECT
id,
instrument_name,
if(ti.teacher_instrument_id IS NULL,0,1) as `Value`
from teaching_instrument_list as til
LEFT JOIN teachers_instruments as ti
on ti.teacher_instrument_id = til.id
Add a column and check for teacher_instrument_id. If found set Value to 1 else 0.

Categories