SQL joins "going up" two tables - sql

I'm trying to create a moderately complex query with joins:
SELECT `history`.`id`,
`parts`.`type_id`,
`serialized_parts`.`serial`,
`history_actions`.`action`,
`history`.`date_added`
FROM `history_actions`, `history`
LEFT OUTER JOIN `parts` ON `parts`.`id` = `history`.`part_id`
LEFT OUTER JOIN `serialized_parts` ON `serialized_parts`.`parts_id` = `history`.`part_id`
WHERE `history_actions`.`id` = `history`.`action_id`
AND `history`.`unit_id` = '1'
ORDER BY `history`.`id` DESC
I'd like to replace `parts`.`type_id` in the SELECT statement with `part_list`.`name` where the relationship I need to enforce between the two tables is `part_list`.`id` = `parts`.`type_id`. Also I have to use joins because in some cases `history`.`part_id` may be NULL which obviously isn't a valid part id. How would I modify the query to do this?
Here is some sample date as requested:
history table:
(source: ianburris.com)
serialized_parts table:
(source: ianburris.com)
parts table:
(source: ianburris.com)
part_list table:
(source: ianburris.com)
And what I want to see is:
id name serial action date_added
4 Battery 567 added 2010-05-19 10:42:51
3 Antenna Board 345 added 2010-05-19 10:42:51
2 Main Board 123 added 2010-05-19 10:42:51
1 NULL NULL created 2010-05-19 10:42:51

This would at least be on the right track...
If you're looking to NOT show any parts with an invalid ID, simply change the LEFT JOINs to INNER JOINs (they will restrict NULL values)
SELECT `history`.`id`
, `parts`.`type_id`
, `part_list`.`name`
, `serialized_parts`.`serial`
, `history_actions`.`action`
, `history`.`date_added`
FROM `history_actions`
INNER JOIN `history` ON `history`.`action_id` = `history_actions`.`id`
LEFT JOIN `parts` ON `parts`.`id` = `history`.`part_id`
LEFT JOIN `serialized_parts` ON `serialized_parts`.`parts_id` = `history`.`part_id`
LEFT JOIN `part_list` ON `part_list`.`id` = `parts`.`type_id`
WHERE `history`.`unit_id` = '1'
ORDER BY `history`.`id` DESC

Boy, these backticks make my eyes hurt.
SELECT
h.id,
p.type_id,
pl.name,
sp.serial,
ha.action,
h.date_added
FROM
history h
INNER JOIN history_actions ha ON ha.id = h.action_id
LEFT JOIN parts p ON p.id = h.part_id
LEFT JOIN serialized_parts sp ON sp.parts_id = h.part_id
LEFT JOIN part_list pl ON pl.id = p.type_id
WHERE
h.unit_id = '1'
ORDER BY
history.id DESC

Related

Use name of controllers to find table name in Select

I want to extract a ID , User_ID value from one of the Companies and Contract tables, depending on the ContorollerName value.
select P.TitleProject, P.StartDateProject, P.EndDateProject,P.ControllerID,P.RecordID,P.IsAllocated,P.ProjectStatus_ID,
CN.ControllerName,CN.PersianName,
PU.ProjectID,PU.UserID,PU.RoleID,
CASE
WHEN CN.ControllerName = 'Company' THEN
Companies.Id,Companies.[User_Id]
WHEN CN.ControllerName = 'Contract' THEN
Contracts.Id,Contracts.[User_Id]
END
from Projects P
left outer join Controllers CN ON P.ControllerID = CN.Id
left outer join ProjectUsers PU ON P.Id = PU.ProjectID
where P.IsAllocated = 1
For example, if ContorollerName is 'Company' , the select command is as follows :
select P.TitleProject, P.StartDateProject, P.EndDateProject,P.ControllerID,P.RecordID,P.IsAllocated,P.ProjectStatus_ID,
CN.ControllerName,CN.PersianName,
PU.ProjectID,PU.UserID,PU.RoleID,
Companies.Id,Companies.[User_Id]
You are on the right track -- using left join. But you need to add the tables to the from clause with the appropriate logic.
The logic for the join is quite unclear. The query looks something like this:
select . . .,
coalesce(c.id, co.id) as id,
coalesce(c.user_id, co.user_id) as user_id
from Projects P left join
Controllers CN
on P.ControllerID = CN.Id left join
ProjectUsers PU
on P.Id = PU.ProjectID left join
companies c
on c.? = ? and -- no idea what the right join conditions are
c.ControllerName = 'Company' left join
contracts co
on co.? = ? and -- no idea what the right join conditions are
co.ControllerName = 'Contract'
where P.IsAllocated = 1

SQL SELECT Statement Datetime Troubles with Inner Joins

I have this query that returns no records because its can't match the grp.EffectiveDate specified while their are Inner Joins within the query. Database is SQL Server.
SELECT grp.GroupID, grp.GroupNumber, grp.Name, grp.Location, grp.GroupTypeID, grp.DivisionID,
grp.MasterGroupID, grp.EffectiveDate, grp.TerminationDate, crt.[ContractNumber], pln.[PBPNumber], div.SiteName, src.Name as SourceName
FROM [Group] grp
INNER JOIN [IndividualPlanDemographic] idp ON grp.GroupID = idp.IndividualPlanDemographicID
INNER JOIN [Plan] pln ON idp.PlanID = pln.PlanID
INNER JOIN [Contract] crt ON pln.ContractID = crt.ContractID
INNER JOIN [Division] div ON grp.DivisionID = div.DivisionID
INNER JOIN [SourceSystem] src ON div.SourceSystemID = src.SourceSystemID
WHERE 1 = 1
AND grp.EffectiveDate = '1/1/2015 12:00:00 AM' AND grp.GroupTypeID = '2' ORDER BY ContractNumber
However, if I just query the main "Group" table, it will return the correct records I'm looking for based on all the criteria.
SELECT grp.GroupID, grp.GroupNumber, grp.Name, grp.Location, grp.GroupTypeID, grp.DivisionID,
grp.MasterGroupID, grp.EffectiveDate, grp.TerminationDate
FROM [Group] grp
WHERE 1 = 1
AND grp.EffectiveDate = '1/1/2015 12:00:00 AM' AND grp.GroupTypeID = '2'
Why will my query not working when using more than one table? I specifically reference the table alias before the column (grp.EffectiveDate) so I don't understand what else is wrong. As always, thank you in advance for your help.
Perhaps one of your INNER JOIN's should be a LEFT OUTER JOIN. When using joins and you want parent records to be returned with/without children records and this is allowed by your schema (nullable FK) then you should left outer join.
SELECT grp.GroupID, grp.GroupNumber, grp.Name, grp.Location, grp.GroupTypeID, grp.DivisionID,
grp.MasterGroupID, grp.EffectiveDate, grp.TerminationDate, crt.[ContractNumber], pln.[PBPNumber], div.SiteName, src.Name as SourceName
FROM [Group] grp
INNER JOIN [IndividualPlanDemographic] idp ON grp.GroupID = idp.IndividualPlanDemographicID
INNER JOIN [Plan] pln ON idp.PlanID = pln.PlanID
INNER JOIN [Contract] crt ON pln.ContractID = crt.ContractID
--THE GROUP IS NOT REQUIRED TO BE IN A DIVISION RELATIONSHIP
LEFT OUTER JOIN [Division] div ON grp.DivisionID = div.DivisionID
LEFT OUTER JOIN [SourceSystem] src ON div.SourceSystemID = src.SourceSystemID
WHERE 1 = 1
AND grp.EffectiveDate = '1/1/2015 12:00:00 AM' AND grp.GroupTypeID = '2' ORDER BY ContractNumber
Ultimately this means that the rows that match this date and group type do not contain records in all the tables that you are INNER JOINed to.
Either your database is missing expected records, or you need to change some of these INNER JOINs to LEFT OUTER JOINs instead.

Left join not returning record with null

Here's the structure of my database (used to collect info from a survey):
question_responses->answer_choices->subquestions->questions->survey
| ^
V |
submissions->response_group<----------------------------survey_deployment
where A->B means A has the PK of B
So, I want to get the responses of a particular user; let's say that his submission pk is 1. I had written the query
select question_responses.answer_text
from submissions join question_responses on (question_responses.submission_pk = submission.pk)
join answer_choices on (question_responses.answer_choices_pk = answer_choices.pk)
join subquestions on (answer_choices.subquestions_pk = subquestions.pk)
right join questions on (subquestions.questions_pk = questions.pk)
where submissions.pk = 1
order by questions.order
It is possible to not answer questions. In such a case, there is no question_responses.pk recorded. I would still like to be able to account for skipping, so I need this record to be returned, even if there is no response. I thought that the right join would have accounted for this record, but apparently not.
Any help is greatly appreciated.
Because you have submissions.pk = 1 in the where clause this effectively turns your outer join into an inner join, since any missing submission will have a pk of null. and null = 1 is not true.
You could rewrite your query using a LEFT JOIN, and selecting from questions:
select question_responses.answer_text
from questions
left join (subquestions
inner join answer_choices
on answer_choices.subquestions_pk = subquestions.pk
inner join question_responses
on question_responses.answer_choices_pk = answer_choices.pk
inner join submissions
on question_responses.submission_pk = submission.pk)
on subquestions.questions_pk = questions.pk
and submissions.pk = 1
order by questions.order;
This is similar to doing something like:
select subquery.answer_text
from questions
left join
( select question_responses.answer_text, subquestions.questions_pk
from subquestions
inner join answer_choices
on answer_choices.subquestions_pk = subquestions.pk
inner join question_responses
on question_responses.answer_choices_pk = answer_choices.pk
inner join submissions
on question_responses.submission_pk = submission.pk
where submissions.pk = 1
) subquery
on subquery.questions_pk = questions.pk
order by questions.order;
but depending on your dbms the former may perform better as it has no derived tables.
Check out this, does it help?
SELECT question_responses.answer_text
FROM questions LEFT JOIN subquestions on (subquestions.questions_pk = questions.pk)
LEFT JOIN answer_choices on (answer_choices.subquestions_pk = subquestions.pk)
LEFT JOIN question_responses on (question_responses.answer_choices_pk = answer_choices.pk)
LEFT JOIN submissions on (question_responses.submission_pk = submission.pk)
WHERE submissions.pk = 1
ORDER BY questions.order

Avoiding NULL records on this LEFT JOIN

The query below works - EXCEPT - it is returning NULL values for vehicle_id. I do not want any records that have NULL for vehicle_id.
Since vehicle_id is tied to fund_series, this is complicated to me.
When I had the vehicle_id conditions underneath the WHERE, the query was not working. Any SQL geniuses that can help?
I put the MIN() aggregate functions in there just so I could get the GROUP BY to work.
SELECT DISTINCT
MIN(ml.pretty_file_name),
ml.filename,
MIN(ml.issued_date),
MIN(mr.rule_name),
MIN(mlob.line_of_business_name),
MIN(mt.media_type_name),
MAX(v.vehicle_name)
FROM Media_Live ml
JOIN Media_Type mt
ON mt.media_type_id = ml.media_type_id
JOIN Media_Rule mr
ON mr.rule_id = ml.rule_id
JOIN Media_Line_Of_Business mlob
ON mlob.line_of_business_id = ml.line_of_business_id
LEFT JOIN Fund_Class_Media fcm
ON fcm.media_id=ml.media_id
LEFT JOIN Fund_Class_Live fc
ON fc.fund_class_id = fcm.fund_class_id
LEFT JOIN Fund_Series fs
ON fs.fund_series_id = fc.fund_series_id
LEFT JOIN Vehicle AS v
ON v.vehicle_id=fs.vehicle_id AND /*THIS IS WHERE IM GETTING NULLS*/
(
v.vehicle_id = 1
OR v.vehicle_id = 2
OR v.vehicle_id = 5
)
LEFT JOIN Media_Media_Tag AS mmt ON mmt.media_id=ml.media_id
LEFT JOIN Media_Tag AS mtag ON mtag.tag_id=mmt.tag_id
WHERE
(/*people can search with terms for fc*/
--fc.fund_class_id LIKE '%'+replace(?,' ','%')+'%'
)
(
mt.media_type_id = 33
OR mt.media_type_id = 1
OR mt.media_type_id = 12
)
AND
(
mr.rule_id = 3
OR mr.rule_id = 9
)
AND
(
mtag.tag_name != 'exclude_web_lit_center'
)
GROUP BY ml.filename
This is what a left join does, allow nulls. Just take out the left join part making it an inner join.
JOIN Vehicle AS v ON v.vehicle_id=fs.vehicle_id AND v.vehicle_id IN (1,2,5)
You cold also do this, but I don't see why you would:
LEFT JOIN Vehicle AS v ON v.vehicle_id=fs.vehicle_id AND ISNULL(v.vehicle_id,0) IN (1,2,5)
In the WHERE clause, add:
AND v.Vehicl_Id IS NOT NULL
That should do it.

Top 1 on Left Join SubQuery

I am trying to take a person and display their current insurance along with their former insurance. I guess one could say that I'm trying to flaten my view of customers or people. I'm running into an issue where I'm getting multiple records back due to multiple records existing within my left join subqueries. I had hoped I could solve this by adding "TOP 1" to the subquery, but that actually returns nothing...
Any ideas?
SELECT
p.person_id AS 'MIRID'
, p.firstname AS 'FIRST'
, p.lastname AS 'LAST'
, pg.name AS 'GROUP'
, e.name AS 'AOR'
, p.leaddate AS 'CONTACT DATE'
, [dbo].[GetPICampaignDisp](p.person_id, '2009') AS 'PI - 2009'
, [dbo].[GetPICampaignDisp](p.person_id, '2008') AS 'PI - 2008'
, [dbo].[GetPICampaignDisp](p.person_id, '2007') AS 'PI - 2007'
, a_disp.name AS 'CURR DISP'
, a_ins.name AS 'CURR INS'
, a_prodtype.name AS 'CURR INS TYPE'
, a_t.date AS 'CURR INS APP DATE'
, a_t.effdate AS 'CURR INS EFF DATE'
, b_disp.name AS 'PREV DISP'
, b_ins.name AS 'PREV INS'
, b_prodtype.name AS 'PREV INS TYPE'
, b_t.date AS 'PREV INS APP DATE'
, b_t.effdate AS 'PREV INS EFF DATE'
, b_t.termdate AS 'PREV INS TERM DATE'
FROM
[person] p
LEFT OUTER JOIN
[employee] e
ON
e.employee_id = p.agentofrecord_id
INNER JOIN
[dbo].[person_physician] pp
ON
p.person_id = pp.person_id
INNER JOIN
[dbo].[physician] ph
ON
ph.physician_id = pp.physician_id
INNER JOIN
[dbo].[clinic] c
ON
c.clinic_id = ph.clinic_id
INNER JOIN
[dbo].[d_Physgroup] pg
ON
pg.d_physgroup_id = c.physgroup_id
LEFT OUTER JOIN
(
SELECT
tr1.*
FROM
[transaction] tr1
LEFT OUTER JOIN
[d_vendor] ins1
ON
ins1.d_vendor_id = tr1.d_vendor_id
LEFT OUTER JOIN
[d_product_type] prodtype1
ON
prodtype1.d_product_type_id = tr1.d_product_type_id
LEFT OUTER JOIN
[d_commission_type] ctype1
ON
ctype1.d_commission_type_id = tr1.d_commission_type_id
WHERE
prodtype1.name <> 'Medicare Part D'
AND tr1.termdate IS NULL
) AS a_t
ON
a_t.person_id = p.person_id
LEFT OUTER JOIN
[d_vendor] a_ins
ON
a_ins.d_vendor_id = a_t.d_vendor_id
LEFT OUTER JOIN
[d_product_type] a_prodtype
ON
a_prodtype.d_product_type_id = a_t.d_product_type_id
LEFT OUTER JOIN
[d_commission_type] a_ctype
ON
a_ctype.d_commission_type_id = a_t.d_commission_type_id
LEFT OUTER JOIN
[d_disposition] a_disp
ON
a_disp.d_disposition_id = a_t.d_disposition_id
LEFT OUTER JOIN
(
SELECT
tr2.*
FROM
[transaction] tr2
LEFT OUTER JOIN
[d_vendor] ins2
ON
ins2.d_vendor_id = tr2.d_vendor_id
LEFT OUTER JOIN
[d_product_type] prodtype2
ON
prodtype2.d_product_type_id = tr2.d_product_type_id
LEFT OUTER JOIN
[d_commission_type] ctype2
ON
ctype2.d_commission_type_id = tr2.d_commission_type_id
WHERE
prodtype2.name <> 'Medicare Part D'
AND tr2.termdate IS NOT NULL
) AS b_t
ON
b_t.person_id = p.person_id
LEFT OUTER JOIN
[d_vendor] b_ins
ON
b_ins.d_vendor_id = b_t.d_vendor_id
LEFT OUTER JOIN
[d_product_type] b_prodtype
ON
b_prodtype.d_product_type_id = b_t.d_product_type_id
LEFT OUTER JOIN
[d_commission_type] b_ctype
ON
b_ctype.d_commission_type_id = b_t.d_commission_type_id
LEFT OUTER JOIN
[d_disposition] b_disp
ON
b_disp.d_disposition_id = b_t.d_disposition_id
WHERE
pg.d_physgroup_id = #PhysGroupID
In Sql server 2005 you can use OUTER APPLY
SELECT p.person_id, s.e.employee_id
FROM person p
OUTER APPLY (SELECT TOP 1 *
FROM Employee
WHERE /*JOINCONDITION*/
ORDER BY /*Something*/ DESC) s
http://technet.microsoft.com/en-us/library/ms175156.aspx
The pattern I normally use for this is:
SELECT whatever
FROM person
LEFT JOIN subtable AS s1
ON s1.personid = person.personid
...
WHERE NOT EXISTS
( SELECT 1 FROM subtable
WHERE personid = person.personid
AND orderbydate > s1.orderbydate
)
Which avoids the TOP 1 clause and maybe makes it a little clearer.
BTW, I like the way you've put this query together in general, except I'd leave out the brackets, assuming you have rationally named tables and columns; and you might even gain some performance (but at least elegance) by listing columns for tr1 and tr2, rather than "tr1.*" and "tr2.*".
Thanks for all of the feedback and ideas...
In the simplest of terms, I have a person table that stores contact information like name, email, etc. I have another table that stores transactions. Each transaction is really an insurance policy that would contain information on the provider, product type, product name, etc.
I want to avoid giving the user duplicate person records since this causes them to look for the duplicates prior to running mail merges, etc. I'm getting duplicates when there is more than 1 transaction that has not been terminated, and when there is more than 1 transaction that has been terminated.
Someone else suggested that I consider a cursor to grab my distinct contact records and then perform the sub selects to get the current and previous insurance information. I don't know if I want to head down that path though.
It's difficult to understand your question so first I'll throw this out there: does changing your SELECT to SELECT DISTINCT do what you want?
Otherwise, let me get this straight, you're trying to get your customers' current insurance and previous insurance, but they may actually have many insurances before that, recorded in the [transactions] table? I looked at your SQL for quite a few minutes but can't figure out what it all means, so could you please reduce it down to only the parts that are necessary? Then I'll think about it some more. It sounds to me like you need a GROUP BY somehow, but I can't work it out exactly yet.
Couldn't take the time to dig through all your SQL (what a beast!), here's an idea that might make things easier to handle:
select
p.person_id, p.name <and other person columns>,
(select <current policy columns>
from pol <and other tables for policy>
where pol.<columns for join> = p.person_id
and <restrictions for current policy>),
(select <previous policy columns>
from pol <and other tables for policy>
where pol.<columns for join> = p.person_id
and <restrictions for previouspolicy>),
<other columns>
from person p <and "directly related" tables>
This makes the statement easier to read by separating the different parts into their own subselects, and it also makes it easier to add a "Top 1" in without affecting the rest of the statement. Hope that helps.