I have below query -
select *
from g
right outer JOIN p
ON
( g.c1 = p.c1
or g.c2=p.c2
or g.c3=p.c3
)
OR condition in ON clause is causing serious performance issue.
I tried with UNION as -
select g.*,p.option_code
from g
right outer JOIN p
ON (g.c1= p.c1)
UNION
select g.*,p.option_code
from g
right outer JOIN p
ON (g.c2=p.c2)
UNION
select g.*,p.option_code
from g
right outer JOIN p
ON (g.c3=p.c3)
IS it possible to realise this join in alternative way.
Best Regards
First, right outer join is so hard to follow logically. So, let's replace this with a left join:
select *
from p left join
g
on g.c1 = p.c1 or g.c2=p.c2 or g.c3 = p.c3;
Assuming you want the first value that matches, you can do something like:
select p.*, coalesce(g1.col, g2.col, g3.col)
from p left join
g g1
on g1.c1 = p.c1 left join
g g2
on g2.c2 = p.c2 left join
g g3
on g3.c3 = p.c3;
You can add as many coalesce() expressions as you need.
This is not 100% the same as your query, but it works in many similar situations.
select distinct *
from
(
select g.*,p.option_code
from g
right outer JOIN p
ON (g.c1= p.c1)
UNION all
select g.*,p.option_code
from g
right outer JOIN p
ON (g.c2=p.c2)
UNION all
select g.*,p.option_code
from g
right outer JOIN p
ON (g.c3=p.c3)
) x
Related
I already LEFT JOINed many tables into two sections, but now how do I join these two together?
I am not sure if subquery would work but at least I tried subquery its very confused/
SELECT MoviePerson.PersonId, MoviePerson.PersonFirstName, MoviePerson.PersonLastName, MoviePersonRole.RoleId FROM MoviePerson
LEFT JOIN MoviePersonRole ON MoviePerson.PersonId = MoviePersonRole.PersonId;
select DVD.DVDId, DVD.GenreId, DVD.RatingId, DVD.DVDTitle, DVD.DVDReleasedate, DVD.TheaterReleaseDate, Genre.GenreName, Rating.RatingName,
Rating.RatingDescription
from DVD
LEFT join Genre on DVD.GenreId = Genre.GenreID
LEFT JOIN Rating ON DVD.RatingId = Rating.RatingId
LEFT JOIN MoviePersonRole ON DVD.DVDId =MoviePersonRole.DVDId
;
db<>fiddle
It doesn't look as though you have a column to join on. If you did, you could use the following syntax:
SELECT *
FROM
(
SELECT MoviePerson.PersonId, MoviePerson.PersonFirstName, MoviePerson.PersonLastName, MoviePersonRole.RoleId FROM MoviePerson
LEFT JOIN MoviePersonRole ON MoviePerson.PersonId = MoviePersonRole.PersonId;
) X
INNER JOIN
(
select DVD.DVDId, DVD.GenreId, DVD.RatingId, DVD.DVDTitle, DVD.DVDReleasedate, DVD.TheaterReleaseDate, Genre.GenreName, Rating.RatingName,
Rating.RatingDescription
from DVD
LEFT join Genre on DVD.GenreId = Genre.GenreID
LEFT JOIN Rating ON DVD.RatingId = Rating.RatingId
LEFT JOIN MoviePersonRole ON DVD.DVDId =MoviePersonRole.DVDId
) Y
ON X.column_to_join = y.column_to_join
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.
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.
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)
I'm trying to select the sum of a column value multiplied by the count of another column
select
sum(CS.revenue * count(C.subid)) as [total_revenue]
from campaigns_history CH
LEFT OUTER JOIN campaign_sends CS ON CS.campaign_historyID = CH.id
LEFT OUTER JOIN c C ON C.subid = cast(CS.id as nchar(255))
WHERE CH.id=19
I got this error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
How can I do this?
Thanks and happy programming! :)
You need to separate the SUM and the COUNT - as such:
select
SUM(CS.revenue) * COUNT(C.subid) AS [total_revenue]
FROM campaigns_history CH
LEFT OUTER JOIN campaign_sends CS ON CS.campaign_historyID = CH.id
LEFT OUTER JOIN c C ON C.subid = CAST(CS.id as NCHAR(255))
WHERE CH.id=19
You're title already gives you the answer :) Calculate the sum and multiply it by count.
SELECT Sum(CS.revenue) * Count(C.subid) as [total_revenue]
FROM campaigns_history CH
LEFT OUTER JOIN campaign_sends CS ON CS.campaign_historyID = CH.id
LEFT OUTER JOIN c C ON C.subid = cast(CS.id as nchar(255))
WHERE CH.id = 19
try this:
select
sum(
CS.revenue *
(select
count(c.subid)
from c
where c.subid = cast(CS.id as nchar(255))
) as [total_revenue]
from campaigns_history CH
LEFT OUTER JOIN campaign_sends CS ON CS.campaign_historyID = CH.id
WHERE CH.id=19
Sorry guys, your answers didint suit my needs.
Its mostly my fault because i didnt make myself clear...
What I really need it's the sum of CS.revenue value multiplied by the count of C.subid.. Does this makes sense? Sorry my rusty english.
In order words first multiply CS.revenue by the count of C.subid and only then sum that result.
I came up with query wich returns what I expect.
SELECT
sum(css.total_revenue)
from (SELECT top 1 * from campaign_sends) as CS
INNER JOIN (
select
CS.revenue * count(C.subid) as [total_revenue]
from campaigns_history CH
LEFT OUTER JOIN campaign_sends CS ON CS.campaign_historyID = CH.id
LEFT OUTER JOIN c C ON C.subid = cast(CS.id as nchar(255))
WHERE
CH.id=19
GROUp BY CS.revenue
) as css ON css.total_revenue is not null