Conditional self join SQL Server - sql

I'm trying to self join with conditions.
Is there a way where I could do this without using Union? (union works fine but the query doubles in size which I'm trying to avoid)
Below is the query I've written (any suggestion or guidance would be much appreciated!)
Select
concat(de.EthnicityText ,' - ', dec2.EthnicityText) as 'Ethnicity'
from
dl.DimEthnicity de
inner join
(select dec1.EthnicityParentId, dec1.EthnicityText
from dl.dimethnicity dec1) as dec2 on de.EthnicityID = dec2.EthnicityParentId
union all
select de.EthnicityText as 'Ethnicity'
from dl.DimEthnicity de
where de.EthnicityParentId is null

Assuming you're wanting to return all values, even if ParentID is null.
select a.EthnicityText + case when b.EthnicityText is null then '' else ' - ' + b.EthnicityText end
from DimEthnicity a
left join DimEthnicity b on b.EthnicityID = a.EthnicityParentID
Left join allows for the condition you're looking for (only return the self join row when it exists). More info on left joins: https://www.w3schools.com/sql/sql_join_left.asp

Related

How to work with Multiple Joins without Duplicate Data SQL

I'm having an issue working with SQL data where once I have completed muptiple joins I am getting duplicate data.
Here is the code written for
SELECT RPPlannedLabor.PeriodHrs, RPPlannedLabor.StartDate, (RPAssignment.WBS1 + ' ' + PR.Name) AS 'WBS1', RPAssignment.WBS2, EM.FirstName, EM.LastName, EM.TKGroup, (EM.FirstName + ' ' + EM.LastName) AS 'Full Name'
FROM RPPlannedLabor
LEFT OUTER JOIN RPAssignment
ON RPPlannedLabor.AssignmentID = RPAssignment.AssignmentID
AND RPAssignment.WBS1 IS NOT NULL
LEFT OUTER JOIN EM
ON RPAssignment.ResourceID = EM.Employee
AND EM.Status = 'a'
LEFT OUTER JOIN PR
ON ((RPAssignment.WBS1 = PR.WBS1)
AND (ISNULL(RPAssignment.WBS2,0) = ISNULL(PR.WBS2,0))
AND (ISNULL(RPAssignment.WBS3,0) = ISNULL(PR.WBS3,0)))
AND PR.Sublevel = 'Y'
Any help would be greatly appreciated :)
I'd have to guess your isnull portions in the join is finding a bunch of null fields and cross joining, but thats just a guess. Data issues like this can't really be solved on a code help forum, best I can do is teach you to trouble shoot.
Run this and get the row count:
SELECT count(1)
FROM RPPlannedLabor
Run this
SELECT count(1)
FROM RPPlannedLabor
LEFT OUTER JOIN RPAssignment
ON RPPlannedLabor.AssignmentID = RPAssignment.AssignmentID
AND RPAssignment.WBS1 IS NOT NULL
Compare with first query...if count increase, your duplicate is on this first join.
Doesn't increase? Keep iterating, run this:
SELECT count(1)
FROM RPPlannedLabor
LEFT OUTER JOIN RPAssignment
ON RPPlannedLabor.AssignmentID = RPAssignment.AssignmentID
AND RPAssignment.WBS1 IS NOT NULL
LEFT OUTER JOIN EM
ON RPAssignment.ResourceID = EM.Employee
AND EM.Status = 'a'
Compare to your count above. Are there more records or is it the same? more records means this last join we added is causing them. If not...my guess is this here is causing the duplicates:
LEFT OUTER JOIN PR
ON ((RPAssignment.WBS1 = PR.WBS1)
AND (ISNULL(RPAssignment.WBS2,0) = ISNULL(PR.WBS2,0))
AND (ISNULL(RPAssignment.WBS3,0) = ISNULL(PR.WBS3,0)))
AND PR.Sublevel = 'Y'
If you are joining on fields with isnull functions, odds are there are nulls and potentially more than one...but I might be off as your data issue could be anywhere.

SQL using table from join in subqueries

I am writing a query to be used as databases view, it looks now like this:
SELECT
contact.*,
contact_users.names AS user_names,
contact_status.status_id AS status_id,
status_translation.name AS status_name,
status_translation.lang_id AS lang_id
FROM contacts as contact
LEFT JOIN contact_status AS contact_status ON contact_status.status_id = contact.status
LEFT JOIN contact_status_translation AS status_translation ON status_translation.id = contact.status
LEFT JOIN (
SELECT
contacts_users.contact_id,
string_agg(users.fullname || ', ' || users.id, ' | ') as names
FROM v_contacts_users as contacts_users
LEFT JOIN v_users as users on users.id = contacts_users.user_id
WHERE users.lang_id = status_translation.lang_id
GROUP BY contacts_users.contact_id
) AS contact_users ON contact_users.contact_id = contact.id
WHERE contact.deleted = FALSE
Everything works as expected except the WHERE condition in the last LEFT JOIN - the WHERE users.lang_id = status_translation.lang_id part says that status_translation cannot be referenced in this part of the query? Why is that? I tried to reference this table with various always but the result is still the same.Thing is that v_users is translated as well so I need to have only one result from this table.
Insert LATERAL between LEFT JOIN and the opening parenthesis if you want to reference previous FROM list entries.

including a condition dynamically based on another condition

I have a query as below
select --------
from table a
left outer join ....c
where
(a.column='123') and (c.column='456')
I would like to
include "(c.column='456')" only when (a.column='123') is not null
how do I do that in a single query ? or do I need to write two separate queries ?
Should be pretty straightforward :
select --------
from table
left outer join....
where (Condition A IS NULL) OR (condition A AND condition B)
UPDATED: For your conditions:
where (a.column is null) or (a.column='123' and c.column='456')
It will include a a row if it's a.column is null or if bot a.column and c.column have valid values.
As I understand your requirement this is the sql you want
select distinct cm.credit_amt,e.credit_lifetime,e.credit_desc,e.icom_code,e.entry_hint,
e.credit_id,e.credit_type_id,e.recontract_period,a.class_desc,a.offer_id,
a.offer_class_id
from rti_retention.package a
left outer join rti_retention.offer_class_credit b on (a.offer_id=b.offer_id
and a.offer_class_id=b.offer_class_id
and a.customer_type_id=b.customer_type_id)
left outer join rti_retention.credit_pre_bundle c on (b.credit_id=c.credit_id)
left outer join rti_retention.credit e on (c.credit_id=e.credit_id)
left outer join rti_retention.credit_mix_amount cm on (cm.credit_id=c.credit_id and cm.prod_mix_id=a.prod_mix_id)
where a.offer_class_id not in (1,2,16)
and a.channel_id=5 and a.customer_type_id=1
and a.offer_id='6055'
and c.prod_mix_id = case when (select count(*)
from rti_retention.credit_pre_bundle c1
where c1.prod_mix_id='1000' ) > 1 then '1000' else c.prod_mix_id end
and e.icom_code is not null
some time there will be some sql syntax errors. due to i havent full data base i wrote sql on mind. cant test it.

Nested Select or Inner Join including rows from another table

I have a pretty complex select statement that returns counted statistics from tables (think of it as an answer bank -- the complex select statement below) using inner join.
These answers are related to a table called Questions_Bank_AnswerChoices (which stores all the questions).
I am attempting to first pull the Questions (from the table Questions_Bank_AnswerChoices) then match them up with the statistics (complex statement below). The complex statement below pulls the statistics, but does not pull the questions unless they have been answered.
So, if no one answers question1, then question one will not show up in the statistics because it is not included in the Answers table (bc no one answered it).
How can I achieve this? I think that I need to outer join?
Complex Select Statement:
WITH tbl as (
SELECT
Questions_Bank.QuestionID, Questions_Bank.QuestionName,
REPLACE(Schools_Answers_Items.AnswerValue, '? ', ', ') as AnswerValue,
COUNT(Schools_Answers_Items.SchoolsAnswersItemID) AS CountAnswer,
Schools_Answers_Items.SchoolID
FROM Questions_Bank
INNER JOIN Schools_Answers_Items
ON Questions_Bank.QuestionID = Schools_Answers_Items.QuestionID
LEFT OUTER JOIN Schools_Answers
ON Schools_Answers_Items.SchoolsAnswerID = Schools_Answers.SchoolsAnswerID
WHERE (Questions_Bank.QuestionID = 1108)
AND (Schools_Answers.SchoolID = 103)
GROUP BY
Schools_Answers_Items.SchoolID,
Schools_Answers_Items.AnswerValue,
Questions_Bank.QuestionID,
Questions_Bank.QuestionName
)
SELECT
QuestionID, QuestionName, AnswerValue, CountAnswer,
SUM(CountAnswer) OVER () AS CountAllAnswers
FROM tbl
Try changing this
INNER JOIN Schools_Answers_Items
ON Questions_Bank.QuestionID = Schools_Answers_Items.QuestionID
to
LEFT OUTER JOIN Schools_Answers_Items
ON Questions_Bank.QuestionID = Schools_Answers_Items.QuestionID
and you might want to remove this
AND (Schools_Answers.SchoolID = 103)
or replace it with this
AND (Schools_Answers.SchoolID = 103 OR Schools_Answers.SchoolID IS NULL)
Try this:
SELECT
Questions_Bank.QuestionID, Questions_Bank.QuestionName,
REPLACE(Schools_Answers_Items.AnswerValue, '? ', ', ') as AnswerValue,
Schools_Answers_Items.SchoolID
FROM Questions_Bank
LEFT OUTER JOIN Schools_Answers_Items
ON Questions_Bank.QuestionID = Schools_Answers_Items.QuestionID
LEFT OUTER JOIN Schools_Answers
ON Schools_Answers_Items.SchoolsAnswerID = Schools_Answers.SchoolsAnswerID
WHERE Schools_Answers_Items.SchoolID

SQL Merge Cells from many rows into a single cell

Similar to the question here:
http://forums.asp.net/t/1580379.aspx/1
I'm trying to merge common cells into a single comma-delimited cell, however across an inner join.
My SQL is:
SELECT DISTINCT tb_Order.OrderNumber, tb_Order.OrderId,
tb_Order.orderDate, tb_Order.OrderTotal,
tb_OrderStatus.OrderStatus, tb_Order.GroupOrderId,
tb_Venue.Title AS Venue
FROM tb_Order INNER JOIN tb_OrderItem ON tb_Order.OrderId = tb_OrderItem.OrderId
INNER JOIN tb_Show ON tb_OrderItem.ShowId = tb_Show.showId
INNER JOIN tb_OrderStatus ON tb_Order.OrderStatusId = tb_OrderStatus.OrderStatusID
INNER JOIN tb_Venue ON tb_Show.VenueId = tb_Venue.id
WHERE (tb_Order.OrderId = 705)
I need the [venue] to be comma-delimited like:
"Interactive Seating Chart Advanced, Interactive Seating Chart Mode Multi-Click"
If you have SQL Server 2017 (14.x) and later, you can use the STRING_AGG function.
SELECT tb_Order.OrderNumber, tb_Order.OrderId,
tb_Order.orderDate, tb_Order.OrderTotal,
tb_OrderStatus.OrderStatus, tb_Order.GroupOrderId,
STRING_AGG(tb_Venue.Title, ',') AS Venue
FROM tb_Order INNER JOIN tb_OrderItem ON tb_Order.OrderId = tb_OrderItem.OrderId
INNER JOIN tb_Show ON tb_OrderItem.ShowId = tb_Show.showId
INNER JOIN tb_OrderStatus ON tb_Order.OrderStatusId = tb_OrderStatus.OrderStatusID
INNER JOIN tb_Venue ON tb_Show.VenueId = tb_Venue.id
WHERE (tb_Order.OrderId = 705)
GROUP BY tb_Order.OrderNumber, tb_Order.OrderId,
tb_Order.orderDate, tb_Order.OrderTotal,
tb_OrderStatus.OrderStatus, tb_Order.GroupOrderId
One way to do this is by using a side effect of row processing order to populate a variable iteratively, like so. The downside of this is that it won't work in a simple query context and it's not the most efficient solution.
DECLARE #venues varchar(max)
SET #venues = ''
SELECT #venues =
CASE WHEN #venues = '' THEN v.Title
ELSE #venues + ',' + v.Title END
FROM tb_Venue v
SELECT #venues
A second way to do this is with STUFF and SQL Server XML extensions, like so:
SELECT DISTINCT v.Title,
(STUFF(
(SELECT ',' + v2.Title
FROM tb_Venue v2
-- uncomment this line if you are going
-- to aggregate only by something in the outer query
-- WHERE v2.GroupKey = v.GroupKey
ORDER BY v2.Title
FOR XML PATH(''), TYPE, ROOT
).value('root[1]','varchar(max)'),1,1,'')) as Aggregation
FROM tb_Venue v
A CLR-based solution is usually the most performant for this use case, and one of those is described here along with a boatload of other less ideal solutions...
Your problem is not one rooted in set logic so there isn't a clean SQL solution...