Unnecessary LEFT JOINs on all Child tables from main LEFT JOIN table required in query for correct result - sql

I am LEFT JOIN-ing the table RECALLS_T with EVENTS_T. Some Recalls do not have any Events, and for those I want a blank row returned.
However, once an entry in EVENTS_T exists, all the following extra tables from EVENTS_T which I also need (ANSWERS_T, ACTIVITY_QUESTIONS_T, ACTIVITES_T) are guaranteed to have entries. For these subsequent tables there is no need to do a LEFT JOIN from their parent, it can be an INNER JOIN just as well.
But if I do the following, the query does not return blank EVENTS_T rows for a RECALLS_T.
SELECT ..
FROM
recalls_t r
LEFT JOIN events_t e ON ( r.id = e.recall_id ) --Only LEFT JOIN this main table
INNER JOIN answers_t ans ON (ans.event_id = e.id)
INNER JOIN activity_questions_t aq ON (ans.activity_question_id = aq.id)
INNER JOIN PUBLIC.activities_t act ON (aq.activity_id = act.id)
Instead, I need to change every subsequent INNER JOIN to a LEFT JOIN as well, in order to get Recalls with blank Events.
SELECT ..
FROM
recalls_t r
LEFT JOIN events_t e ON ( r.id = e.recall_id )
LEFT JOIN answers_t ans ON (ans.event_id = e.id)
LEFT JOIN activity_questions_t aq ON (ans.activity_question_id = aq.id)
LEFT JOIN PUBLIC.activities_t act ON (aq.activity_id = act.id)
I'm just not clear on why I have to do this. The top-level optional join is all I care about, but the downstream joins from EVENTS_T are guaranteed to have data provided the Event exists. Shouldn't the top-level single EVENTS_T join be enough? I'm using Postgres.

The reason you need to do this is because rows preserved by the outer join will have NULL for all columns from events - so inner joining using an equality condition on e.id will remove them from the result again.
you can move the ON clause to the end of the query to give your desired semantics.
The left join is then on the virtual result of inner joining events_t, answers_t, activity_questions_t and activities_t
SELECT ...
FROM recalls_t r
LEFT JOIN events_t e
INNER JOIN answers_t ans
ON ( ans.event_id = e.id )
INNER JOIN activity_questions_t aq
ON ( ans.activity_question_id = aq.id )
INNER JOIN PUBLIC.activities_t act
ON ( aq.activity_id = act.id )
ON ( r.id = e.recall_id )
Or you could consider a RIGHT JOIN here instead
SELECT ...
FROM events_t e
INNER JOIN answers_t ans
ON ( ans.event_id = e.id )
INNER JOIN activity_questions_t aq
ON ( ans.activity_question_id = aq.id )
INNER JOIN PUBLIC.activities_t act
ON ( aq.activity_id = act.id )
RIGHT JOIN recalls_t r
ON ( r.id = e.recall_id )

You have to do this because answers_t`` is joining onevents_s. If thejoinkey (e.recall_id) isNULL`, then there will be no match. And the inner join will not return the row. And so on for the other tables.
You seem to understand the fix. Once you use left join, you need to continue using left join for those tables that are connected to the second table of the left join.

Related

How do you work out the average of a sum function within a temp table?

I have created a temp table that lists each client's invoice(s), plus the number of days it took to pay the invoice. A client can have more than one invoice.
Instead of this, I would just like the temp table to list each client once, along with the AVERAGE number of days it took to pay all of their invoices.
Any tips on how to do this would be much appreciated.
Thanks
select
c.client_code,
b.bill_num,
b.bill_date,
ba.TRAN_DATE,
sum(datediff(Day,b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
into #tempG1
from blt_bill b
left outer join blt_billm bm on b.tran_uno = bm.bill_tran_uno
left outer join BLT_BILL_AMT ba on bm.BILLM_UNO = ba.BILLM_UNO
left outer join hbm_matter m on bm.matter_uno = m.matter_uno
left outer join hbm_client c on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0
and bm.ar_status NOT IN ('P','X')
and ba.TRAN_TYPE in ('CR','crx')
group by c.client_code,b.bill_num,b.bill_date,ba.TRAN_DATE
select * from #tempG1
Drop Table #tempG1
I am not familiar with temp tables, but this should work (tested on a simliar scenario on MySQL8 and assuming that #tempG1 return results):
select
c.client_code,
b.bill_num,
b.bill_date,
ba.TRAN_DATE,
sum(datediff(Day,b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
from blt_bill b
left outer join blt_billm bm on b.tran_uno = bm.bill_tran_uno
left outer join BLT_BILL_AMT ba on bm.BILLM_UNO = ba.BILLM_UNO
left outer join hbm_matter m on bm.matter_uno = m.matter_uno
left outer join hbm_client c on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0
and bm.ar_status NOT IN ('P','X')
and ba.TRAN_TYPE in ('CR','crx')
group by c.client_code,b.bill_num,b.bill_date,ba.TRAN_DATE
into #tempG1
############################
SELECT temp.client_code, AVG(temp.Days_To_Pay)
FROM (select * from #tempG1) as temp
GROUP BY temp.client_code
############################
#### Do you see results if drop? ####
Drop Table #tempG1
Note that I put #tempG1, at the bottom of your SELECT request, but might not be what want to achieve, not sure if you want to include your JOIN conditions or not.
Or you could do without temp table(including your join conditions):
SELECT temp.client_code, AVG(temp.Days_To_Pay)
(
select
c.client_code,
b.bill_num,
b.bill_date,
ba.TRAN_DATE,
sum(datediff(Day,b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
from blt_bill b
left outer join blt_billm bm on b.tran_uno = bm.bill_tran_uno
left outer join BLT_BILL_AMT ba on bm.BILLM_UNO = ba.BILLM_UNO
left outer join hbm_matter m on bm.matter_uno = m.matter_uno
left outer join hbm_client c on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0
and bm.ar_status NOT IN ('P','X')
and ba.TRAN_TYPE in ('CR','crx')
group by c.client_code,b.bill_num,b.bill_date,ba.TRAN_DATE
) as temp
GROUP BY temp.client_code
This sounds like a simple aggregation:
select c.client_code, avg(datediff(Day, b.BILL_DATE, ba.TRAN_DATE)) as Days_To_Pay
from blt_bill b join
blt_billm bm
on b.tran_uno = bm.bill_tran_uno join
BLT_BILL_AMT ba
on bm.BILLM_UNO = ba.BILLM_UNO join
hbm_matter m on bm.matter_uno = m.matter_uno join
hbm_client c
on m.client_uno = c.client_uno
where b.total_bill_amt > 0.0 and
bm.ar_status not in ('P', 'X') and
ba.TRAN_TYPE in ('CR', 'crx')
group by c.client_code;
Note that you do not need outer joins. The where clause is turning most of them into inner joins anyway. Plus, if you are aggregating by the client code, then presumably you want a non-NULL value.

Duplicate data from select statement

I want to make a stress test to a procedure than generate a .csv file.
The problem is that i have not enough data, so i want to duplicate data in my sql select .
The query look like this:
SELECT P.FST_NAME,
P.LAST_NAME,
P.EMAIL_ADDR,
P.PERSON_UID,
PR.FST_NAME PRSP_FST_NAME,
PR.LAST_NAME PRSP_LAST_NAME,
M.X_BAPRO_DT_01,
M.X_BAPRO_DT_02,
M.X_BAPRO_DT_03,
M.X_BAPRO_MONTO,
M.X_BAPRO_NUM_01,
M.X_BAPRO_NUM_02,
M.X_BAPRO_NUM_03,
M.X_BAPRO_TEXT_01,
M.X_BAPRO_TEXT_02,
M.X_BAPRO_TEXT_03,
M.X_BAPRO_TEXT_04,
M.X_BAPRO_TEXT_05
FROM SIEBEL.S_SRC C
left join SIEBEL.S_CAMP_CON M on C.ROW_ID = M.SRC_ID
left join SIEBEL.S_DMND_CRTN_PRG T on T.ROW_ID = M.DCP_ID
left join SIEBEL.S_CONTACT P on P.ROW_ID = M.CON_PER_ID
left join SIEBEL.S_PRSP_CONTACT PR on PR.ROW_ID= M.PRSP_CON_PER_ID
WHERE
C.ROW_ID <> p_row_id
So, This query return about 100 records, i want to retrive 1000 records and i dont really care if the data is duplicated.
You can add a cross join:
FROM SIEBEL.S_SRC C
left join SIEBEL.S_CAMP_CON M on C.ROW_ID = M.SRC_ID
left join SIEBEL.S_DMND_CRTN_PRG T on T.ROW_ID = M.DCP_ID
left join SIEBEL.S_CONTACT P on P.ROW_ID = M.CON_PER_ID
left join SIEBEL.S_PRSP_CONTACT PR on PR.ROW_ID= M.PRSP_CON_PER_ID
cross join (select 1 as n from dual union all
select 2 from dual
. . .
) x
You can also use the VALUE clause to construct the little "muliplier"-table as shown below:
SELECT ...
FROM SIEBEL.S_SRC C
left join SIEBEL.S_CAMP_CON M on C.ROW_ID = M.SRC_ID
left join SIEBEL.S_DMND_CRTN_PRG T on T.ROW_ID = M.DCP_ID
left join SIEBEL.S_CONTACT P on P.ROW_ID = M.CON_PER_ID
left join SIEBEL.S_PRSP_CONTACT PR on PR.ROW_ID= M.PRSP_CON_PER_ID
cross join (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) tabl(n)

POSTGRESQL - UNNEST function not work in LINUX [duplicate]

When i run this query in window system behave correctly UNNSET
but when i run this query Linux behave different.unnset duplicate record list on different row
SELECT DISTINCT
"billing_billmanagement"."creation_date",
"billing_billmanagement"."bill_number",
unnest(array_agg(DISTINCT "inventory_product"."product_name")) AS "product",
unnest(array_agg(DISTINCT "services_service"."name")) AS "service"
FROM "billing_billmanagement"
INNER JOIN "users_staffuser" ON ("billing_billmanagement"."staff_id" = "users_staffuser"."id")
INNER JOIN "auth_user" ON ("users_staffuser"."user_id" = "auth_user"."id")
LEFT OUTER JOIN "billing_customerproductbill" ON ("billing_billmanagement"."id" = "billing_customerproductbill"."bill_id")
LEFT OUTER JOIN "inventory_product" ON ("billing_customerproductbill"."product_id" = "inventory_product"."id")
LEFT OUTER JOIN "billing_customerservicebill" ON ("billing_billmanagement"."id" = "billing_customerservicebill"."bill_id")
LEFT OUTER JOIN "services_service" ON ("billing_customerservicebill"."service_id" = "services_service"."id")
WHERE "billing_billmanagement"."creation_date" BETWEEN '2017-12-04' AND '2017-12-06'
GROUP BY billing_billmanagement.creation_date,
billing_billmanagement.bill_number
ORDER BY "billing_billmanagement"."creation_date" ASC
If getting duplicate rows is the problem, try this
SELECT billing_billmanagement.creation_date,
billing_billmanagement.bill_number,
inventory_product.product_name AS product,
services_service.name AS service
FROM billing_billmanagement
INNER JOIN users_staffuser ON (billing_billmanagement.staff_id = users_staffuser.id)
INNER JOIN auth_user ON (users_staffuser.user_id = auth_user.id)
LEFT OUTER JOIN billing_customerproductbill ON (billing_billmanagement.id = billing_customerproductbill.bill_id)
LEFT OUTER JOIN inventory_product ON (billing_customerproductbill.product_id = inventory_product.id)
LEFT OUTER JOIN billing_customerservicebill ON (billing_billmanagement.id = billing_customerservicebill.bill_id)
LEFT OUTER JOIN services_service ON (billing_customerservicebill.service_id = services_service.id)
WHERE billing_billmanagement.creation_date BETWEEN '2017-12-04' AND '2017-12-06'
GROUP BY 1,
2,
3,
4
ORDER BY 1 ASC;

IN Linux Distinct SQL is not working with UNNEST

When i run this query in window system behave correctly UNNSET
but when i run this query Linux behave different.unnset duplicate record list on different row
SELECT DISTINCT
"billing_billmanagement"."creation_date",
"billing_billmanagement"."bill_number",
unnest(array_agg(DISTINCT "inventory_product"."product_name")) AS "product",
unnest(array_agg(DISTINCT "services_service"."name")) AS "service"
FROM "billing_billmanagement"
INNER JOIN "users_staffuser" ON ("billing_billmanagement"."staff_id" = "users_staffuser"."id")
INNER JOIN "auth_user" ON ("users_staffuser"."user_id" = "auth_user"."id")
LEFT OUTER JOIN "billing_customerproductbill" ON ("billing_billmanagement"."id" = "billing_customerproductbill"."bill_id")
LEFT OUTER JOIN "inventory_product" ON ("billing_customerproductbill"."product_id" = "inventory_product"."id")
LEFT OUTER JOIN "billing_customerservicebill" ON ("billing_billmanagement"."id" = "billing_customerservicebill"."bill_id")
LEFT OUTER JOIN "services_service" ON ("billing_customerservicebill"."service_id" = "services_service"."id")
WHERE "billing_billmanagement"."creation_date" BETWEEN '2017-12-04' AND '2017-12-06'
GROUP BY billing_billmanagement.creation_date,
billing_billmanagement.bill_number
ORDER BY "billing_billmanagement"."creation_date" ASC
If getting duplicate rows is the problem, try this
SELECT billing_billmanagement.creation_date,
billing_billmanagement.bill_number,
inventory_product.product_name AS product,
services_service.name AS service
FROM billing_billmanagement
INNER JOIN users_staffuser ON (billing_billmanagement.staff_id = users_staffuser.id)
INNER JOIN auth_user ON (users_staffuser.user_id = auth_user.id)
LEFT OUTER JOIN billing_customerproductbill ON (billing_billmanagement.id = billing_customerproductbill.bill_id)
LEFT OUTER JOIN inventory_product ON (billing_customerproductbill.product_id = inventory_product.id)
LEFT OUTER JOIN billing_customerservicebill ON (billing_billmanagement.id = billing_customerservicebill.bill_id)
LEFT OUTER JOIN services_service ON (billing_customerservicebill.service_id = services_service.id)
WHERE billing_billmanagement.creation_date BETWEEN '2017-12-04' AND '2017-12-06'
GROUP BY 1,
2,
3,
4
ORDER BY 1 ASC;

Left Join not working

I am a SQL Server beginner and so I have been using MS Access to create queries and then have been amending them for SQL Server.
I have created a query which includes a left join, which works perfectly in Access but when I copy the SQL code across to SQL Server it does not show the null values and I cannot work out why.
I want to show all of the attribute values whether they include a value or not. Can anyone help? The code is as follows:
SELECT DISTINCT
tbLease.LeaseTitle
, tbAttributeValue.AttributeTemplateDefinitionLinkID
, tbAttributeValue.Value
FROM
((((tbBusinessUnit
LEFT JOIN
tbAttributeValue ON tbBusinessUnit.BusinessUnitID = tbAttributeValue.ParentID)
INNER JOIN
tbBuildingLinkBusinessUnit ON tbBusinessUnit.BusinessUnitID = tbBuildingLinkBusinessUnit.BusinessUnitID)
INNER JOIN
tbBuilding ON tbBuildingLinkBusinessUnit.BuildingID = tbBuilding.BuildingID)
INNER JOIN
(tbUnitLocation
INNER JOIN
tbUnit ON tbUnitLocation.UnitID = tbUnit.UnitID)
ON tbBuilding.BuildingID = tbUnitLocation.LocationID)
INNER JOIN
tbLease ON tbUnit.UnitID = tbLease.UnitID
WHERE
(((tbAttributeValue.AttributeTemplateDefinitionLinkID) = 30
Or (tbAttributeValue.AttributeTemplateDefinitionLinkID) = 31
Or (tbAttributeValue.AttributeTemplateDefinitionLinkID) = 32));
Hard to say what you want without some examples but you might want this:
WHERE coalesce(tbAttributeValue.AttributeTemplateDefinitionLinkID,30) in (30,31,32);
This will give you items where AttributeTemplateDefinitionLinkID is null
(that is it did not join on the left join) OR is one of those 3 values.
Right now if you don't join with the left join it will not display that row because of the where condition, so your left join is the same as an inner join.
The NULL values you would normally see with a left join are getting filtered out by your WHERE clause.
Any time you add filters to a WHERE clause that apply to an outer joined table, it will effectively make it the same as an inner join unless you include NULL values as an option in your where clause as well.
SELECT DISTINCT
tbLease.LeaseTitle,
tbAttributeValue.AttributeTemplateDefinitionLinkID,
tbAttributeValue.Value
FROM
tbBusinessUnit
LEFT JOIN tbAttributeValue ON tbBusinessUnit.BusinessUnitID = tbAttributeValue.ParentID
INNER JOIN tbBuildingLinkBusinessUnit ON tbBusinessUnit.BusinessUnitID = tbBuildingLinkBusinessUnit.BusinessUnitID
INNER JOIN tbBuilding ON tbBuildingLinkBusinessUnit.BuildingID = tbBuilding.BuildingID
INNER JOIN tbUnitLocation ON tbBuilding.BuildingID = tbUnitLocation.LocationID
INNER JOIN tbUnit ON tbUnitLocation.UnitID = tbUnit.UnitID
INNER JOIN tbLease ON tbUnit.UnitID = tbLease.UnitID
WHERE
tbAttributeValue.AttributeTemplateDefinitionLinkID in (30, 31, 32)
or tbAttributeValue.AttributeTemplateDefinitionLinkID is null
Move the right table filters from where clause to ON condition when you are using Left Outer Join else Left join will be implicitly converted to INNER JOIN. Try this
SELECT DISTINCT tblease.leasetitle,
tbattributevalue.attributetemplatedefinitionlinkid,
tbattributevalue.value
FROM tbbusinessunit
LEFT JOIN tbattributevalue
ON tbbusinessunit.businessunitid = tbattributevalue.parentid
AND ( tbattributevalue.attributetemplatedefinitionlinkid IN
( 30, 31, 32 )
OR tbattributevalue.attributetemplatedefinitionlinkid IS
NULL )
INNER JOIN tbbuildinglinkbusinessunit
ON tbbusinessunit.businessunitid =
tbbuildinglinkbusinessunit.businessunitid
INNER JOIN tbbuilding
ON tbbuildinglinkbusinessunit.buildingid = tbbuilding.buildingid
INNER JOIN tbunitlocation
ON tbbuilding.buildingid = tbunitlocation.locationid
INNER JOIN tbunit
ON tbunitlocation.unitid = tbunit.unitid
INNER JOIN tblease
ON tbunit.unitid = tblease.unitid