I have an Update query that will help me to update a certain field and right now it is throwing an error on the last two lines of code where the d.column is mentioned. Does anyone know how I can still use the d.column fields at the end or know a work around that will produce the same results? Help is greatly appreciated. The error is where I am using d.LastUpdateSchedule and d.Due_Dte in the last two lines of code.
update ods.Customer
set NumberTPD = a.NumberCode
from ods.Customer r
left join
(
Select d.CustomerNumber
, d.due_Dte
, l.NumberCode
from (select r.CustomerNumber
, r.due_Dte
, r.NumberTPD
, max(l.UpdateSchedule) as LastUpdateSchedule
from ods.Customer r
left join ods.CustomerHistory l
on r.CustomerNumber = l.CustomerNumber
and r.due_Dte >= l.UpdateSchedule
and l.Examine = 1
and r.ExamineFrequency in ('MONTHLY','MNTHLYLDAY')
and isnull(r.ScheduleEndDate,'1970-01-01') < r.due_Dte
group by r.CustomerNumber, r.Due_Dte, r.NumberTPD) d
left join ods.CustomerHistory l
on d.CustomerNumber = l.CustomerNumber
and d.LastUpdateSchedule = l.UpdatedSchedule
) a
on r.CustomerNumber = a.CustomerNumber
and r.Due_Dte = a.Due_Dte
where d.Due_Dte > '2018-08-03'
and d.LastUpdateSchedule is not null
this is because your table d is inside your table a move your where clause to table d
update ods.Customer
set NumberTPD = a.NumberCode
from ods.Customer r
left join
(Select d.CustomerNumber
, d.due_Dte
, l.NumberCode
from
(select r.CustomerNumber
, r.due_Dte
, r.NumberTPD
, max(l.UpdateSchedule) as LastUpdateSchedule
from ods.Customer r
left join ods.CustomerHistory l on r.CustomerNumber = l.CustomerNumber
and r.due_Dte >= l.UpdateSchedule
and l.Examine = 1
and r.ExamineFrequency in ('MONTHLY','MNTHLYLDAY')
and isnull(r.ScheduleEndDate,'1970-01-01') < r.due_Dte
group by r.CustomerNumber, r.Due_Dte, r.NumberTPD) d
left join ods.CustomerHistory l on d.CustomerNumber = l.CustomerNumber
and d.LastUpdateSchedule = l.UpdatedSchedule
where d.Due_Dte > '2018-08-03'
and d.LastUpdateSchedule is not null
) a on r.CustomerNumber = a.CustomerNumber
and r.Due_Dte = a.Due_Dte
-- only what you need
UPDATE cust
SET cust.NumberTPD = histRow.NumberCode
FROM ods.Customer cust
JOIN
(SELECT c.CustomerNumber,
max(h.UpdateSchedule) AS LastUpdateSchedule
FROM ods.Customer c
JOIN ods.CustomerHistory h ON c.CustomerNumber = h.CustomerNumber
AND c.due_Dte >= h.UpdateSchedule
WHERE c.Due_Dte > '2018-08-03'
AND c.ExamineFrequency IN ('MONTHLY',
'MNTHLYLDAY')
AND isnull(c.ScheduleEndDate, '1970-01-01') < c.due_Dte --sure about this?
AND h.Examine = 1
GROUP BY c.CustomerNumber) maxes
JOIN ods.CustomerHistory histRow ON maxes.CustomerNumber = histRow.CustomerNumber
AND maxes.LastUpdateSchedule = histRow.UpdatedSchedule
I need to select extra columns from another table in my sql query.
SELECT
d.UnitID,
b.BookingID,
d.ProjectID,
b.ClientName,
(SELECT LetterTypeID
FROM Letters
WHERE ProjectID = 27 AND BookingID = b.BookingID)
FROM
ScheduledDues AS d
INNER JOIN
Booking AS b ON d.BookingID = b.BookingID
INNER JOIN
Units AS u ON d.UnitID = u.UnitID
WHERE
d.ProjectID = 27
AND DueFrom <= GETDATE()
GROUP BY
d.BookingID, d.UnitID, d.ProjectID,
u.UnitNo, b.ClientName
HAVING
SUM(DueTill) = 0
How can I do this? and have it in group by. Is selecting LetterTypeID possible?
Need to use LetterTypeID in group by like below or You can fetch results without letters table into temp and join it with letters to get LetterTypeID
SELECT d.UnitID,
b.BookingID,
d.ProjectID,
b.ClientName,
l.LetterTypeID
FROM ScheduledDues AS d INNER JOIN
Booking AS b ON d.BookingID = b.BookingID INNER JOIN
Units AS u ON d.UnitID = u.UnitID INNER JOIN
Letters AS l ON b.BookingID=l.BookingID AND l.ProjectID=27
WHERE d.ProjectID = 27 AND
DueFrom <= GETDATE()
GROUP BY d.BookingID,
d.UnitID,
d.ProjectID,
u.UnitNo,
b.ClientName,
l.LetterTypeID
HAVING SUM(DueTill) = 0
Can you try this query
SELECT BookingID, UnitID, ProjectID
UnitNo, ClientName ,LetterTypeID FROM (SELECT
d.UnitID,
b.BookingID,
d.ProjectID,
b.ClientName,
DueTill,
(SELECT LetterTypeID
FROM Letters
WHERE ProjectID = 27 AND BookingID = b.BookingID) LetterTypeID
FROM
ScheduledDues AS d
INNER JOIN
Booking AS b ON d.BookingID = b.BookingID
INNER JOIN
Units AS u ON d.UnitID = u.UnitID
WHERE
d.ProjectID = 27
AND DueFrom <= GETDATE()) x
GROUP BY
BookingID, UnitID, ProjectID,
UnitNo, ClientName ,LetterTypeID
HAVING
SUM(DueTill) = 0
I have to convert the correlated sub-query to non-correlated sub-query cuz of performance issues .
like that :
The correlated sub-query :(So slow ) returns 4000 row
SELECT a.personid,a.name,b.conid,d.condat,e.connam
FROM main_empr a INNER JOIN coninr b
ON a.personid = b.personid AND a.calc_year = b.calc_year
INNER JOIN mainconinr c
ON b.conid = c.conid
INNER JOIN coninr d
ON a.personid = d.personid AND a.calc_year = d.calc_year
INNER JOIN mainconinr e
ON d.conid = e.conid
WHERE c.active_flag = 1 and c.endreward_flag = 1
AND d.condat = (SELECT MIN(bb.condat) FROM coninr bb WHERE bb.personid = b.personid AND bb.calc_year = b.calc_year AND ((bb.conid > 0 AND bb.conid < 4 ) OR (bb.conid IN(16,6) )) )
AND b.condat = (SELECT MAX(bb.condat) FROM coninr bb WHERE bb.personid = b.personid AND bb.calc_year = b.calc_year AND ((bb.conid > 0 AND bb.conid < 4 ) OR (bb.conid IN(16,6) )) )
AND ( 0 = ( SELECT COUNT(*) FROM servmain x WHERE x.personid = a.personid AND x.calc_year = a.calc_year )
OR b.condat > ( SELECT MAX(x.serv_date) FROM servmain x WHERE x.personid = a.personid AND x.calc_year = a.calc_year ) )
AND a.calc_year = 2018
The non-correlated query :returns about 12300 rows!!
SELECT a.personid,a.name,b.conid,d.condat,e.connam
FROM main_empr a INNER JOIN
coninr b
ON a.personid = b.personid AND a.calc_year = b.calc_year
INNER JOIN mainconinr c
ON b.conid = c.conid
INNER JOIN coninr d
ON a.personid = d.personid AND a.calc_year = d.calc_year
INNER JOIN mainconinr e ON d.conid = e.conid
INNER JOIN
(SELECT MAX(bb.condat) AS condat ,bb.personid,bb.calc_year ,bb.conid
FROM coninr bb
GROUP BY bb.personid,bb.calc_year,bb.conid
)Max_cont
ON Max_cont.personid = b.personid AND Max_cont.calc_year = b.calc_year AND Max_cont.condat = b.condat AND ((Max_cont.conid > 0 AND Max_cont.conid < 4 ) OR (Max_cont.conid IN(16,6) ))
INNER JOIN
(SELECT MIN(dd.condat) AS condat ,dd.personid,dd.calc_year,dd.conid
FROM coninr dd GROUP BY dd.personid,dd.calc_year,dd.conid
)Min_cont
ON Min_cont.personid = d.personid AND Min_cont.calc_year = d.calc_year AND Min_cont.condat = d.condat AND ((Min_cont.conid > 0 AND Min_cont.conid < 4 ) OR (Min_cont.conid IN(16,6) ))
WHERE c.active_flag = 1 and c.endreward_flag = 1
AND ( 0 = ( SELECT COUNT(*) FROM servmain x WHERE x.personid = a.personid AND x.calc_year = a.calc_year )
OR b.condat > ( SELECT MAX(x.serv_date) FROM servmain x WHERE x.personid = a.personid AND x.calc_year = a.calc_year ) )
AND a.calc_year = 2018
The problem is :
I use the coninr table twice to get the last and the first contract date in the same row .
It works fine in the first query but it was so slow because of the correlated sub-query,but in the second query it brings more than one row for the same person one of them for the first contract date and the other for the last one !!
How to fix this problem ?
This looks reasonable, but I've no way to know how it'll perform:
SELECT a.personid,a.name,b.conid,d.condat,e.connam
FROM main_empr a INNER JOIN coninr b
ON a.personid = b.personid AND a.calc_year = b.calc_year
INNER JOIN mainconinr c
ON b.conid = c.conid
INNER JOIN coninr d
ON a.personid = d.personid AND a.calc_year = d.calc_year
INNER JOIN mainconinr e
ON d.conid = e.conid
inner join
(
SELECT bb.personid, bb.calc_year, bb.conid, MIN(bb.condat) MinDate, MAX(bb.condat) MaxDate
FROM coninr bb WHERE
where (bb.conid > 0 and bb.conid < 4) or (bb.conid in (6, 16))
group by bb.personid, bb.calc_year, bb.conid
) zz on d.concat = zz.MinDate and b.condat = zz.MaxDate and b.personid = zz.personid and b.calc_year = zz.calc_year
left outer join
(
select s.personid, s.calc_year, max(s.serv_date) MaxServDate
from servmain s
group by s.personid, s.calc_year
) s on a.personid = s.personid and a.calc_year = s.calc_year
WHERE c.active_flag = 1 and c.endreward_flag = 1
and (s.MaxServDate is null or b.condat ? s.MaxServDate
AND a.calc_year = 2018
You don't need two queries for table coninr, you can get min and max in the same query with the group by. Also, for ServMain, doing a left outer join and putting in the where that either it's null (equivalent to count(*) = 0) or is less than b.condat takes care of that.
I have a sql query for a report, it includes a few sub queries. it runs very slow. I tried a few ways (like use join instead of subquery, add a few more index). but none of them worked. Here is the query:
declare #time_from datetime
declare #time_to datetime
set #time_from ='2012-01-01'
set #time_to = '2014-01-01'
select a.a_id, c.c_id, c.c_chat_line_id, a.a_first_name, a.a_last_name
,(select isnull(SUM(ac.ac_amount),0) from t_actress_credit ac join t_order o on o.o_id = ac.order_id where o.o_status = 1 and ac.actress_id = a.a_id and ac.ac_time>=#time_from and ac.ac_time<=#time_to) as credit
,(select isnull(SUM(ac.ac_amount),0) from t_actress_credit ac join t_order o on o.o_id = ac.order_id where o.o_status = 1 and ac.ac_is_paid = 1 and ac.actress_id = a.a_id and ac.ac_time>=#time_from and ac.ac_time<=#time_to) as paid_credit
,(select COUNT(1) from t_message pm join t_call_log l1 on pm.call_log_id = l1.c_id where pm.m_type = 2 and l1.caller_id = c.c_id and pm.m_time>=#time_from and pm.m_time<=#time_to) as pmsg_sent
,(select COUNT(1) from t_message pm join t_call_log l2 on pm.m_to_call_log_id = l2.c_id where pm.m_type = 2 and l2.caller_id = c.c_id and pm.m_time>=#time_from and pm.m_time<=#time_to) as pmsg_received
,(select COUNT(1) from t_message pm join t_call_log l3 on pm.call_log_id = l3.c_id where pm.m_type = 1 and l3.caller_id = c.c_id and pm.m_time>=#time_from and pm.m_time<=#time_to) as lcmsg_sent
,(select COUNT(1) from t_message pm join t_call_log l4 on pm.m_to_call_log_id = l4.c_id where pm.m_type = 1 and l4.caller_id = c.c_id and pm.m_time>=#time_from and pm.m_time<=#time_to) as lcmsg_received
,(select COUNT(1) from t_actress_live_minute where actress_id = a.a_id and alm_time>=#time_from and alm_time<=#time_to ) as live_calls
,(select isnull(SUM(alm_minutes),0) from t_actress_live_minute where actress_id = a.a_id and alm_time>=#time_from and alm_time<=#time_to) as live_call_minutes
,(select isnull(count(1),0) from t_call_log l where l.caller_id = c.c_id and l.c_time_out is not null and c_time_in >=#time_from and c_time_in <= #time_to) as total_calls
,(select isnull(SUM(DATEDIFF(minute, l.c_time_in, l.c_time_out)),0) from t_call_log l where c_time_in >=#time_from and c_time_in <= #time_to and l.caller_id = c.c_id and l.c_time_out is not null ) as total_call_minutes
from t_actress a
join t_caller c on c.c_id = a.caller_id
group by a.a_id,c.c_id, c.c_chat_line_id, a.a_first_name, a.a_last_name
Can any one give some suggestions?
Thanks a lot!
Alan
You could try combining subqueries that pull from the same table or set of tables into a single subquery. To account for variations in the conditions, you could use conditional aggregation (employing CASE expressions).
I can see four, possibly five, such groups in your query. Here it is rewritten to use four subqueries:
SELECT
a.a_id,
c.c_id,
c.c_chat_line_id,
a.a_first_name,
a.a_last_name,
ISNULL(cr.credit , 0) AS credit
ISNULL(cr.paid_credit , 0) AS paid_credit
ISNULL(m.pmsg_sent , 0) AS pmsg_sent,
ISNULL(m.pmsg_received , 0) AS pmsg_received,
ISNULL(m.lcmsg_sent , 0) AS lcmsg_sent,
ISNULL(m.lcmsg_received , 0) AS lcmsg_received,
ISNULL(alm.live_calls , 0) AS live_calls,
ISNULL(alm.live_call_minutes, 0) AS live_call_minutes,
ISNULL(l.total_calls , 0) AS total_calls,
ISNULL(l.total_call_minutes , 0) AS total_call_minutes,
FROM t_actress AS a
INNER JOIN t_caller AS c
ON c.c_id = a.caller_id
LEFT JOIN (
SELECT
ac.actress_id,
SUM( ac.ac_amount ) AS credit,
SUM(CASE ac.ac_is_paid WHEN 1 THEN ac.ac_amount END) AS paid_credit
FROM t_actress_credit AS ac
JOIN t_order o ON o.o_id = ac.order_id
WHERE o.o_status = 1
AND ac.ac_time BETWEEN #time_from AND #time_to
GROUP BY ac.actress_id
) AS ac
ON ac.actress_id = a.a_id
LEFT JOIN (
SELECT
l.caller_id,
COUNT(CASE WHEN m. call_log_id = l1.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_sent,
COUNT(CASE WHEN m.m_to_call_log_id = l2.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_received,
COUNT(CASE WHEN m. call_log_id = l3.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_sent,
COUNT(CASE WHEN m.m_to_call_log_id = l4.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_received
FROM t_message AS m
JOIN t_call_log AS l ON l.c_id IN (m.call_log_id, m.m_to_call_log_id)
WHERE m.m_type IN (1, 2)
AND m.m_time BETWEEN #time_from AND #time_to
GROUP BY l.caller_id
) AS m
ON m.caller_id = c.c_id
LEFT JOIN (
SELECT
actress_id,
COUNT(*) AS live_calls,
SUM(alm_minutes) AS live_call_minutes
FROM t_actress_live_minute
WHERE alm_time BETWEEN #time_from AND #time_to
GROUP BY actress_id
) AS alm
ON alm.actress_id = a.a_id
LEFT JOIN (
SELECT
caller_id,
COUNT(*) AS total_calls,
SUM(DATEDIFF(MINUTE, c_time_in, c_time_out)) AS total_call_minutes
FROM t_call_log
WHERE c_time_out IS NOT NULL
AND c_time_in BETWEEN #time_from AND #time_to
GROUP BY caller_id
) AS l
ON l.actress_id = a.a_id
;
It could be five subqueries if you split the m subquery into two by joining separately on call_log_id and on m_to_call_log_id (and thus potentially giving the query planner more room for optimisation), i.e. instead of
LEFT JOIN (
SELECT
l.caller_id,
COUNT(CASE WHEN m. call_log_id = l1.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_sent,
COUNT(CASE WHEN m.m_to_call_log_id = l2.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_received,
COUNT(CASE WHEN m. call_log_id = l3.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_sent,
COUNT(CASE WHEN m.m_to_call_log_id = l4.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_received
FROM t_message AS m
JOIN t_call_log AS l ON l.c_id IN (m.call_log_id, m.m_to_call_log_id)
WHERE m.m_type IN (1, 2)
AND m.m_time BETWEEN #time_from AND #time_to
GROUP BY l.caller_id
) AS m
ON m.caller_id = c.c_id
it would be
LEFT JOIN (
SELECT
l.caller_id,
COUNT(CASE WHEN m.call_log_id = l1.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_sent,
COUNT(CASE WHEN m.call_log_id = l3.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_sent,
FROM t_message AS m
JOIN t_call_log AS l ON l.c_id = m.call_log_id
WHERE m.m_type IN (1, 2)
AND m.m_time BETWEEN #time_from AND #time_to
GROUP BY l.caller_id
) AS mf
ON mf.caller_id = c.c_id
LEFT JOIN (
SELECT
l.caller_id,
COUNT(CASE WHEN m.m_to_call_log_id = l2.c_id AND m.m_type = 2 THEN 1 END) AS pmsg_received,
COUNT(CASE WHEN m.m_to_call_log_id = l4.c_id AND m.m_type = 1 THEN 1 END) AS lcmsg_received
FROM t_message AS m
JOIN t_call_log AS l ON l.c_id = m.m_to_call_log_id
WHERE m.m_type IN (1, 2)
AND m.m_time BETWEEN #time_from AND #time_to
GROUP BY l.caller_id
) AS mt
ON mt.caller_id = c.c_id
changing also the corresponding references in the main SELECT clause.
I'm not sure whether which of the variations is better, you'll need to test both to find out.
Note that I've omitted the main query's GROUP BY clause. It seems unnecessary both in your query and in mine, because, as far as I can see, it includes primary keys from both t_actress and t_caller and those combinations would be unique anyway. I assume that the GROUP BY is a leftover from your previous attempts at rewriting the query using joins.
thanks for your reply. I tried your way, it is still slow. here is what I did and finally works. I basically put all the sub queries into table, and then join the table at the end. not sure why, but it is faster: here is the code;
-- total calls
declare #t_call table(
a_id bigint,
total_calls bigint,
total_call_minutes bigint
);
insert into #t_call
SELECT a_id, COUNT(1) AS total_calls, isnull(SUM(DATEDIFF(MINUTE, c_time_in, c_time_out)),0) AS total_call_minutes
FROM t_actress aa
join t_call_log l on aa.caller_id = l.caller_id and c_time_in BETWEEN #time_from AND #time_to and c_time_out IS NOT NULL
GROUP BY a_id;
-- total live minutes
declare #t_live table(
a_id bigint,
live_calls bigint,
live_call_minutes bigint
);
insert into #t_live
SELECT a_id, COUNT(*) AS live_calls, isnull(SUM(alm_minutes),0) AS live_call_minutes
FROM t_actress a
join t_actress_live_minute alm on alm.actress_id = a.a_id and alm_time BETWEEN #time_from AND #time_to
GROUP BY a_id
-- total message by caller
declare #t_cm table(
caller_id bigint,
pmsg_sent bigint,
pmsg_received bigint,
lcmsg_sent bigint,
lcmsg_received bigint
)
insert into #t_cm
SELECT l.caller_id,
COUNT(CASE WHEN m.call_log_id = l.c_id AND m.m_type = 2 THEN 1 END) AS _pmsg_sent,
COUNT(CASE WHEN m.m_to_call_log_id = l.c_id AND m.m_type = 2 THEN 1 END) AS _pmsg_received,
COUNT(CASE WHEN m.call_log_id = l.c_id AND m.m_type = 1 THEN 1 END) AS _lcmsg_sent,
COUNT(CASE WHEN m.m_to_call_log_id = l.c_id AND m.m_type = 1 THEN 1 END) AS _lcmsg_received
FROM t_message m
join t_call_log l on l.c_id in (m.call_log_id, m.m_to_call_log_id)
where m.m_time BETWEEN #time_from AND #time_to
GROUP BY l.caller_id
-- total message by actress
declare #t_msg table(
a_id bigint,
pmsg_sent bigint,
pmsg_received bigint,
lcmsg_sent bigint,
lcmsg_received bigint
)
insert into #t_msg
select a_id, isnull(SUM(cm.pmsg_sent),0), isnull(SUM(cm.pmsg_received),0), isnull(SUM(cm.lcmsg_sent),0), isnull(SUM(cm.lcmsg_received),0)
from t_actress a
join #t_cm cm on a.caller_id = cm.caller_id
group by a_id
-- total credit
declare #t_credit table(
a_id bigint,
credit money,
paid_credit money
)
insert into #t_credit
SELECT a_id, isnull(SUM(ac.ac_amount),0) AS credit, isnull(SUM(CASE ac.ac_is_paid WHEN 1 THEN ac.ac_amount else 0 END),0) AS paid_credit
FROM t_actress a
join t_actress_credit ac on ac.actress_id = a.a_id AND ac.ac_time BETWEEN #time_from AND #time_to
JOIN t_order o ON o.o_id = ac.order_id and o_status = 1
GROUP BY a_id
-- the report
select a.a_id, cl.c_id, cl.c_chat_line_id, a.a_first_name, a.a_last_name,
isnull(ac.credit,0) credit, isnull(ac.paid_credit,0) paid_credit,
isnull(m.pmsg_sent,0) pmsg_sent, isnull(m.pmsg_received,0) pmsg_received, isnull(m.lcmsg_sent,0) lcmsg_sent, isnull(m.lcmsg_received,0) lcmsg_received,
isnull(l.live_calls,0) live_calls, isnull(l.live_call_minutes,0) live_call_minutes,
isnull(c.total_calls,0) total_calls, isnull(c.total_call_minutes,0) total_call_minutes
from t_actress a
join t_caller cl on cl.c_id = a.caller_id
left outer join #t_call c on c.a_id = a.a_id
left outer join #t_live l on l.a_id = a.a_id
left outer join #t_msg m on m.a_id = a.a_id
left outer join #t_credit ac on ac.a_id = a.a_id
order by a_id