Update with join takes long time - sql

I have a problem updating a table. I'm running the following query:
UPDATE Table1 SEt entrena = c.Count
FROM Table1 AS p INNER JOIN (
SELECT e.EmplID, COUNT(e.EmplID ) as Count
FROM Table2 AS e WHERE e.Start >= #Start AND e.Start <=#End
GROUP BY e.EmplID ) AS c ON p.EmplID = c.EmplID
WHERE P.Date = '2050-12-31'
The Table1 has 12000 rows and the select inside the join get only 51 rows but the update takes around 2 minutes, but if I delete the where clase p.date = '2050-12-31' the update takes less than a second. And I can't figure out how to solve it. I'm using SQL Server 2008.
Both tables don't have indexes.

This is your query:
UPDATE Table1
SET entrena = c.Count
FROM Table1 p INNER JOIN
(SELECT e.EmplID, COUNT(e.EmplID ) as Count
FROM Table2 e
WHERE e.Start >= #Start AND e.Start <=#End
GROUP BY e.EmplID
) c
ON p.EmplID = c.EmplID
WHERE P.Date = '2050-12-31';
First, you need to change the first line to:
UPDATE p
when you define an alias in the from clause, you need to use that in the update for the right thing to happen. (I wish your query generated an error in SQL Server, but it does not.)
To optimize this query, you want to add indexes. I would suggest these two:
Table1(date, EmplID)
Table2(EmplId, Start)
You may then find that the correlated subquery version is faster, particularly if the where clause is highly selective:
UPDATE p
SET entrena = (SELECT COUNT(e.EmplID ) as Count
FROM Table2 e
WHERE e.Start >= #Start AND e.Start <=#End AND
p.EmplID = e.EmplID
)
FROM Table1 p
WHERE P.Date = '2050-12-31';
However, I suspect that the p versus table1 is the root of your performance problem.

Related

Count with subselect really slow in postgres

I have this query:
SELECT c.name, COUNT(t.id)
FROM Cinema c
JOIN CinemaMovie cm ON cm.cinema_id = c.id
JOIN Ticket t ON cm.id = cinema_movie_id
WHERE cm.id IN (
SELECT cm1.id
FROM CinemaMovie cm1
JOIN Movie m1 ON m1.id = cm1.movie_id
JOIN Ticket t1 ON t1.cinema_movie_id = cm1.id
WHERE m1.name = 'Hellboy'
AND t1.time >= timestamp '2019-04-18 00:00:00'
AND t1.time <= timestamp '2019-04-18 23:59:59' )
GROUP BY c.id;
and the problem is that this query runs really slow (more than 1 minute) when the table has like 20 million rows. From what I understand, the problem seems to be the inner query, as it takes a long time. Also, I have all indexes on foreign keys. What am I missing ?
Also note that when I select only by name (I omit the date) everything takes like 10 seconds.
EDIT
What I am trying to do, is count number of tickets for each cinema name, based on movie name and the timestamp on ticket.
I don't understand why you are using a subquery. Does this do what you want?
SELECT c.name, COUNT(t.id)
FROM Cinema c JOIN
CinemaMovie cm
ON cm.cinema_id = c.id JOIN
Ticket t
ON cm.id = cinema_movie_id JOIN
Movie m
ON m.id = cm.movie_id
WHERE m.name = 'Hellboy' AND
t.time >= '2019-04-18'::timestamp and
t.time < '2019-04-19'::timestamp
GROUP BY c.id, c.name;

Self join on joined table

My query looks like
Select m.cw_sport_match_id as MatchId,
m.season_id as SeasonId,
s.title as SeasonName,
c.title as ContestName
from dbo.cw_sport_match m
inner join dbo.cw_sport_season s
ON m.season_id = s.cw_sport_season_id
inner join dbo.cw_sport_contest c
ON m.contest_id = c.cw_sport_contest_id
Where s.date_start <= GETDATE() AND s.date_end >= GETDATE()
order by s.date_start
No i need the name parent of the sport_contest (if there is one, it can be null). So basically a self join but no on the same table as the query is for. All the examples that i find do the self join are not done on another table.
can any sql pro help me?
So how can i join the cw_sport_season itself with the season_parent_id and get the title of it?
If I'm understanding your question correctly, you want to outer join the cw_sport_season table to itself using the season_parent_id field. Maybe something on these lines:
Select m.cw_sport_match_id as MatchId,
m.season_id as SeasonId,
s.title as SeasonName,
parent.title as ParentSeasonName,
c.title as ContestName
from dbo.cw_sport_match m
inner join dbo.cw_sport_season s
ON m.season_id = s.cw_sport_season_id
inner join dbo.cw_sport_contest c
ON m.contest_id = c.cw_sport_contest_id
left join dbo.cw_sport_season parent
ON s.season_parent_id = parent.cw_sport_season_id
Where s.date_start <= GETDATE() AND s.date_end >= GETDATE()
order by s.date_start

adding another where condition on inner join query

i got an issue with selecting data from below query based on mtb.mid .
all i want is to select below query base on highest mtb.mid but i cant figure it out where do i need to put that cause on my where clause or other place ....
here is my fine and working query that give me result that i need to add a filter that i talked above .
SELECT DISTINCT TOP (100) PERCENT wTB.Week, wTB.Description, wTB.wid, mTB.mid
FROM yTB INNER JOIN
mTB ON yTB.yid = mTB.yid INNER JOIN
wTB ON mTB.mid = wTB.mid INNER JOIN
dTB ON wTB.wid = dTB.wid
WHERE (dTB.dateEn <= CAST(GETDATE() AS DATE))
ORDER BY wTB.Week
Thanks in advance.
This will work.
SELECT DISTINCT TOP (100) PERCENT wTB.Week, wTB.Description, wTB.wid, mTB.mid
FROM yTB INNER JOIN
mTB ON yTB.yid = mTB.yid INNER JOIN
wTB ON mTB.mid = wTB.mid INNER JOIN
dTB ON wTB.wid = dTB.wid
WHERE (dTB.dateEn <= CAST(GETDATE() AS DATE)) AND
mTB.mid = (SELECT MAX(mid)
FROM mTB)
ORDER BY wTB.Week
just put that condition in WHERE clause and use Subquery to get highest mTB.mid value from mTB table.

My CASE statement is wrong. Any idea what I am doing wrong?

I am in a logjam.
When I run the following query, it works:
select DISTINCT l.Seating_Capacity - (select count(*)
from tblTrainings t1, tbllocations l
where l.locationId = t1.LocationId) as
availableSeats
from tblTrainings t1, tbllocations l
where l.locationId = t1.LocationId
However, we would like to add a CASE statement that says, when Seating_Capacity - total count as shown above = 0 then show 'FULL' message.
Otherwise, show remaining number.
Here is that query:
select DISTINCT case when l.Seating_Capacity - (select count(*)
from tblTrainings t1, tbllocations l
where l.locationId = t1.LocationId) = 0 then 'full' else STR(Seating_Capacity) end)
availableSeats
from tblTrainings t1, tbllocations l
where l.locationId = t1.LocationId
I am getting 'Incorrect syntax near ')' which is near 'End'
I am also getting an error that the inner Seating_Capacity is invalid column name.
Your assistance is greatly appreciated.
I must have been in a dream land because I thought it was working during testing.
Now, the app is live and it isn't working.
Thanks a lot in advance
select DISTINCT l.LocationId,c.courseId, c.coursename, l.Seating_Capacity - (select count(*)
from tblTrainings t1
where l.locationId = t1.LocationId and c.courseId = t1.courseId) as
availableSeats,d.dateid,d.trainingDates,d.trainingtime,c.CourseDescription,
i.instructorName,l.location,l.seating_capacity
from tblLocations l
Inner Join tblCourses c on l.locationId = c.locationId
left join tblTrainings t on l.locationId = t.LocationId and c.courseId = t.courseId
Inner Join tblTrainingDates d on c.dateid=d.dateid
Inner Join tblCourseInstructor ic on c.courseId = ic.CourseId
Inner Join tblInstructors i on ic.instructorId = i.instructorId
WHERE CONVERT(VARCHAR(10), d.trainingDates, 101) >= CONVERT(VARCHAR(10), GETDATE(), 101)
To avoid repeating the expression, you can use a WITH clause to simplify your query:
WITH (
-- Start with your query that already works
SELECT DISTINCT l.Seating_Capacity - (select count(*)
from tblTrainings t1, tbllocations l
where l.locationId = t1.LocationId) AS availableSeats
FROM tblTrainings t1, tbllocations l
WHERE l.locationId = t1.LocationId
) AS source
SELECT
-- Add a CASE statement on top of it
CASE WHEN availableSeats = 0 THEN 'Full'
ELSE STR(availableSeats)
END AS availableSeats
FROM source
You have an extra ) at the end of your case statement remove that.
0 then 'full' else STR(Seating_Capacity) end)
^^^
for Seating_Capacity try accessing it with table alias like l.Seating_Capacity
I think you are over complicating the query with your subquery. As I understand it then the following should work as you need:
SELECT AvailableSeats = CASE WHEN l.Seating_Capacity - COUNT(*) = 0 THEN 'Full'
ELSE STR(l.Seating_Capacity - COUNT(*))
END
FROM tblTrainings t1
INNER JOIN tblLocations l
ON l.LocationID = t1.LocationID
GROUP BY l.Seating_Capacity;
I have changed your else to STR(l.Seating_Capacity - COUNT(*)) because I assume you want to know the seats remaining, rather than just the capacity? If I have misinterpreted the requirement, just change it to STR(l.Seating_Capacity).
I have also switched your ANSI 89 implicit joins to ANSI 92 explicit joins, the standard changed over 20 years, and there are plenty of good reasons to switch to the newer syntax. But for completeness the ANSI 89 version of the above query would be:
SELECT AvailableSeats = CASE WHEN l.Seating_Capacity - COUNT(*) = 0 THEN 'Full'
ELSE STR(l.Seating_Capacity - COUNT(*))
END
FROM tblTrainings t1, tblLocations l
WHERE l.LocationID = t1.LocationID
GROUP BY l.Seating_Capacity;
EDIT
To adapt your full query you can simply replace your subquery in the select, with a joined subquery:
SELECT l.LocationId,
c.courseId,
c.coursename,
CASE WHEN l.Seating_Capacity - t.SeatsTaken = 0 THEN 'Full'
ELSE STR(l.Seating_Capacity - t.SeatsTaken)
END AS availableSeats,
d.dateid,
d.trainingDates,
d.trainingtime,
c.CourseDescription,
i.instructorName,
l.location,
l.seating_capacity
FROM tblLocations l
INNER JOIN tblCourses c
ON l.locationId = c.locationId
LEFT JOIN
( SELECT t.LocationID, t.CourseID, SeatsTaken = COUNT(*)
FROM tblTrainings t
GROUP BY t.LocationID, t.CourseID
) t
ON l.locationId = t.LocationId
AND c.courseId = t.courseId
INNER JOIN tblTrainingDates d
ON c.dateid=d.dateid
INNER JOIN tblCourseInstructor ic
ON c.courseId = ic.CourseId
INNER JOIN tblInstructors i
ON ic.instructorId = i.instructorId
WHERE d.trainingDates >= CAST(GETDATE() AS DATE);
JOINs tend to optimise better than correlated subqueries (although sometimes the optimiser can determine that a JOIN would work instead), it also means that you can reference the result (SeatsTaken) multiple times without re-evaluating the subquery.
In addition, by moving the count to a subquery, and removing the join to tblTrainings I think you eliminate the need for DISTINCT which should improve the performance.
Finally I have changed this line:
WHERE CONVERT(VARCHAR(10), d.trainingDates, 101) >= CONVERT(VARCHAR(10), GETDATE(), 101)
To
WHERE d.trainingDates >= CAST(GETDATE() AS DATE);
I don't know if you do, but if you had an index on d.TrainingDates then by converting it to varchar to compare it to today you remove the ability for the optimiser to use this index, since you are only saying >= midnight today, there is no need to perform any conversion on d.TrainingDates, and all you need to do is remove the time portion of GETDATE(), which can be done by casting to DATE. More on this is contained in this article (Yet another gem from Aaron Bertrand)

Date comparison function in SQL Server

I am trying to display records which are created after Oct 1 2010. But my query doesn't seem to work. It also display records from 2004 - Sept 2010 which is not wanted.
What is wrong with the query below?
select Distinct app.app_id,
(convert(varchar, creation_date,101) + ' ' + convert(varchar,creation_date ,108)) as creation_date,
dbo.oea_fn_get_amc_mem_name(app.app_id,primary_msn,getdate(), 'EN', 30000) PIName,
dbo.oea_fn_get_pid_countyname(app.app_id,primary_msn,'OC')as PIpid,
primary_msn,
zip,
home_tel,
work_tel,
work_extn,
other_contact,
other_ext,
cell_tel,
dbo.oea_fn_get_amc_mem_name(app.app_id,mem.msn,getdate(), 'EN', 30000)as Kname,
dbo.oea_fn_get_pid_countyname(app.app_id,mem.msn,'OC')as Knamepid,
mem.msn as Kmsn,
(select count(reminder_id) from reminders (nolock) where app_id=app.app_id) as reminder
from app_application app (nolock)
inner join app_member mem with (nolock) on app.app_id=mem.app_id
--left outer join Oea_App_Program_Disposition disp with (nolock) on mem.app_id = disp.app_id and mem.msn=disp.msn
inner join app_address aadd with (nolock) on app.app_id=aadd.app_id
--inner join app_calc_results calc with (nolock) on mem.app_id=calc.app_id and calc.msn=mem.msn
left outer join app_member_benefits ben with (nolock) on mem.app_id = ben.app_id and mem.msn=ben.msn
where
isnull(mem.coverage_required,0) = 1
and app.app_status = 's'
and ben.ins_end_date < getdate()
and app.client_id = 30000
and app.app_id not in (select app_id from app_renewal)
and (mem.msn in (select calc.msn from app_calc_results calc
inner join app_application app on calc.app_id = app.app_id and calc.prog_id = 'CK' and calc.opt_out = 1))
and (mem.msn in (select msn from app_calc_results where app_id=app.app_id and status not in ('A','X')))
or (mem.msn in (select msn from Oea_App_Program_Disposition where app_id=app.app_id and disp_status not in ('A','P')) )
and app.creation_date >= '10/01/2010'
Thanks for all the help.
You probably want this:
and (
(mem.msn in (select calc.msn from app_calc_results calc
inner join app_application app on calc.app_id = app.app_id and calc.prog_id = 'CK' and calc.opt_out = 1))
or (mem.msn in (select msn from app_calc_results where app_id=app.app_id and status not in ('A','X')))
or (mem.msn in (select msn from Oea_App_Program_Disposition where app_id=app.app_id and disp_status not in ('A','P')) )
)
and app.creation_date >= '10/01/2010'
The problem is with the logic behind the or in the where clause.
As others have stated, the problem is likely the Or clause in the Where clause. In effect, your query is:
Select ...
From ..
Where (A And B And C
And D And E
And F And G
And app.creation_date >= '10/01/2010'
)
Or mem.msn In (
Select msn
From Oea_App_Program_Disposition
Where app_id=app.app_id
And disp_status not in ('A','P')
)
Thus, if for any row, if the Or is true, the rest of the "Ands" are ignored. I would assume that the Or is supposed to be paired with one of the And clauses.