What is wrong with this sql innerjoin? - sql-server-2005

Here is my select statement with the innerjoin of two tables,
if not exists(select EmpId from SalaryDetails
where EmpId in (select Emp_Id
from Employee where Desig_Id=#CategoryId))
begin
// some statements here
end
else
begin
SELECT e.Emp_Id, e.Identity_No, e.Emp_Name,
case WHEN e.SalaryBasis=1 THEN 'Weekly'
ELSE 'Monthly' end as SalaryBasis,e.FixedSalary,
(SELECT TOP 1 RemainingAdvance
FROM SalaryDetails
ORDER BY CreatedDate DESC) as Advance
FROM Employee as e inner join Designation as d on e.Desig_Id=d.Desig_Id
INNER JOIN SalaryDetails as S on e.Emp_Id=S.EmpId
End
My results pane,
alt text http://img220.imageshack.us/img220/7774/resultpane.jpg
And My SalaryDetails Table,
alt text http://img28.imageshack.us/img28/770/salarydettable.jpg
EDIT:
My Output must be,
16 CR14 Natarajan Weekly 150.00
354.00 17 cr12333 Pandian Weekly 122.00 0.00

You're not filtering the sub-query (SELECT TOP 1 RemainingAdvance FROM SalaryDetails ORDER BY CreatedDate DESC) on any employee ID, so it's giving you the first record in the entire table when sorted by CreatedDate DESC (which I'm guessing is 354.)
You will probably want to move that table expression into your FROM clause, not your SELECT, include your employee ID, and do a join on that expression.
SELECT
e.Emp_Id,e.Identity_No,e.Emp_Name,case WHEN e.SalaryBasis=1 THEN 'Weekly' ELSE 'Monthly' end as SalaryBasis,e.FixedSalary,
from Employee as e inner join Designation as d on e.Desig_Id=d.Desig_Id
inner join SalaryDetails as S on e.Emp_Id=S.EmpId
inner join
(SELECT EmpID, RemainingAdvance, RANK() OVER (PARTITION BY EmpID ORDER BY CreatedDate DESC) AS SalaryRank FROM SalaryDetails ORDER BY CreatedDate DESC) as Advance ON Advance.EmpID = e.Emp_ID AND Advance.SalaryRank = 1
This is just off the top of my head so may take a bit of tweaking to run correctly. Note also the use of the RANK() function - if you use TOP 1, you're only ever getting the first record of the entire table. What you need is the first record per employee ID.
If this was me I would probably make that table expression a view or even a scalar-valued function taking your employee ID and returning the first RemainingAdvance value, then you could use TOP 1 and filter on the employee ID.

It looks like your join to Designation isn't even used and you're also missing your WHERE clause that you used in the IF statement at the top. I'd also move the subquery down into the join like Andy pointed out. Without having the DB to test against this probably won't be exact but I'd rewrite it to something like;
SELECT e.Emp_Id, e.Identity_No, e.Emp_Name,
case WHEN e.SalaryBasis=1
THEN 'Weekly'
ELSE 'Monthly' end as SalaryBasis,
e.FixedSalary,S.RemainingAdvance as Advance
FROM Employee as e
INNER JOIN (
SELECT TOP 1 EmpId, RemainingAdvance
FROM SalaryDetails
ORDER BY CreatedDate DESC) as S on e.Emp_Id=S.EmpId
WHERE e.Desig_Id=#CategoryId
Andy's suggestion to move the subquery into a view is a good one, much easier to read and probably a lot more efficient if the DB is large.
EDIT: (ANSWER)
(SELECT sd.empid,
sd.remainingadvance,
ROW_NUMBER() OVER (PARTITION BY sd.empid ORDER BY sd.createddate DESC) AS rank
FROM SALARYDETAILS sd
JOIN EMPLOYEE e ON e.emp_id = sd.empid
AND e.desig_id = #CategoryId) s
WHERE s.rank = 1
I edited jay's answer because he came close to my output...

Related

How to 'detect' a change of value in a column in SQL?

im new to SQL, i wanted to ask:
I have combined multiple tables with CTE and join and resulting on this Image here.
From this table, I wanted to detect and count how many workers changed the category from the 1st or 2nd job.
For example, Jonathan Carey has 'Sales Lapangan' as his first job_category, and changed to 'other' on his 2nd job, i wanted to count this job_category change as one.
I tried Case when, and while but i'm getting more confused.
This is my syntax for the table i created:
with data_apply2 as(with data_apply as(with all_apply as(with job_id as(select job_category,
row_number() over(order by job_category) as job_id
from job_post
group by job_category)
select jp.*, job_id.job_id from job_post jp
join job_id
on job_id.job_category=jp.job_category)
select ja.worker_id, wk.name, ja.id as id_application, aa.job_category, aa.job_id
from job_post_application ja
join all_apply aa
on aa.id=ja.job_post_id
join workers wk
on wk.id = ja.worker_id
order by worker_id,ja.id)
select *,
row_number() over(partition by worker_id order by worker_id) as worker_num
from data_apply)
Thank You
You can group by worker and check the number of distinct job categories:
SELECT worker_id,
COUNT(DISTINCT job_category) > 1 category_change
FROM data_apply
GROUP BY worker_id;
select case when job_category<> job_category then 1 else 0 end as cnt
from
(
select
worker_id,
name,
id_application,
job_category,
job_id,
worker_num,
coalesce(lag(job_category) over(partition by worker_id order by id_application), job_category) as job_category
from
sales_table
) x
This should help, using the Lag function I'm accessing the data over the previous row. and comparing it with the job_category and if they are not equal we are counting them as 1.

SQL Server : finding consecutive absence counts for students over custom dates

I have a table which stores attendances of students for each day. I need to get students who are consecutively absent for 3 days. However, the dates when attendance is taken is not in a order, some days like nonattendance, holidays, weekends are excluded. The dates when students attended are the dates where records exist in that table .
The data is like
StudentId Date Attendance
-----------------------------------------
178234 1/1/2017 P
178234 5/1/2107 A
178234 6/1/2107 A
178234 11/1/2107 A
178432 1/1/2107 P
178432 5/1/2107 A
178432 6/1/2107 P
178432 11/1/2107 A
In the above case the result should be
StudentId AbsenceStartDate AbsenceEndDate ConsecutiveAbsences
----------------------------------------------------------------------------
178234 5/1/2017 11/1/2017 3
I have tried to implement this solution Calculating Consecutive Absences in SQL However that only worked for dates in order only. Any suggestions will be great, thanks
Oh, you have both absences and presents in the table. You can use the difference of row_numbers() approach:
select studentid, min(date), max(date)
from (select a.*,
row_number() over (partition by studentid order by date) as seqnum,
row_number() over (partition by studentid, attendance order by date) as seqnum_a
from attendance a
) a
where attendance = 'A'
group by studentid, (seqnum - seqnum_a)
having count(*) >= 3;
The difference of row numbers gets consecutive values that are the same. This is a little tricky to understand, but if you run the subquery, you should see how the difference is constant for consecutive absences or presents. You only care about absences, hence the where in the outer query.
try this:
declare #t table (sid int, d date, att char(1))
insert #t (sid,d, att) values
(178234, '1/1/2017','P'),
(178234, '5/1/2017','A'),
(178234, '6/1/2017','A'),
(178234, '11/1/2017','A'),
(178432, '1/1/2017','P'),
(178432, '5/1/2017','A'),
(178432, '6/1/2017','P'),
(178432, '11/1/2017','A')
Select s.sid, Min(s.d) startDt, Max(e.d) endDt, s.att, e.att, count(*)
from #t s join #t e on e.d <=
(select max(d) from #t m
Where sid = s.sid
and d > s.d
and att = 'A'
and not exists
(Select * from #t
where sid = s.sid
and d between s.d and m.d
and att = 'P'))
Where s.att = 'A'
and s.d = (Select Min(d) from #t
Where sid = s.sid
and d < e.d
and att = 'A')
group by s.sid, s.d, s.att, e.att
this is also tricky to explain:
basically, it joins the table to itself using aliases s (for start) and e (for end), where the s-row is the first row in a set of contiguous absences, and the e. rows are all following absences that are before the next date where the stud is present. This will generate a set of all the 'A' that do not have a P row within them. Then the sql groups by the appropriate values to return the earliest and latest date, and the count of rows, in each group.
The last where clause ensures that the s row is the first row in the group.

need query syntax to fetch latest by PKID after joining multiple tables

I have 3 tables:
select * from company
select * from emp_profile
select * from emp_salary_upgrade_tracker
table 1, company_pkid, company_code, company_name
table 2, emp_profile_pkid,company_fk_id, emp_number, emp_name, salary
table 3, salary_pk_id,emp_profile_fk_id,emp_number, old_salary, current salary
whenever an employee's salary is changed, its tracked in emp_salary_upgrade_tracker.
I need to write a query to fetch
company_code, emp_number, emp_name, old_salary, current salary
Here the result should have the latest entry ,ie latest salary change from emp_salary_upgrade_tracker.
After joining the tables, i need to fetch the latest from emp_salary_upgrade_tracker(order by pkid may be).
But am clueless of the query syntax. Please help
Use a subquery to get lates ids:
select c.company_code,
e.emp_number,
e.emp_name,
t.old_salary,
t.current_salary
from(select emp_profile_fk_id, max(salary_pk_id) as salary_pk_id
from emp_salary_upgrade_tracker group by emp_profile_fk_id) m
join emp_salary_upgrade_tracker t on m.salary_pk_id = t.salary_pk_id
join emp_profile e on t.emp_profile_fk_id = e.emp_profile_pk_id
join company c on e.company_fk_id = c.company_pkid
Here's a way using row_number to select the row containing the latest value in the emp_salary_upgrade_tracker table when ordered by its primary key.
select * from (
select *,
row_number() over (partition by esut.emp_number order by esut.salary_pk_id desc) rn
from emp_salary_upgrade_tracker esut
join emp_profile ep on ep.emp_profile_pk_id = esut.emp_profile_fk_id
join company c on c.company_pk_id = ep.company_fk_id
) t where rn = 1

Display rows with the highest date attribute for the ones that share the same first name

I want to display the record with the highest slot date for the records that share the same first name. How would I proceed on doing that?
Use can use the ROW_NUMBER windowed function to accomplish this.
See: http://www.postgresql.org/docs/9.3/static/sql-expressions.html#SYNTAX-WINDOW-FUNCTIONS
and http://www.postgresql.org/docs/9.3/static/tutorial-window.html
SELECT *
FROM
(
SELECT E.firstname
, S.category
, S.showname
, O.slotdate
, ROW_NUMBER() over(PARTITION BY E.firstname ORDER BY O.slotdate DESC) as rowNum
FROM hostshows H , Shows S , Host E ,timeslot O
WHERE S.shownumber = E.shownumber
AND H.empnum = E.empnum
AND O.shownumber = H.shownumber
) t
WHERE rowNum = 1
;
This is a textbook example for DISTINCT ON:
SELECT DISTINCT ON (e.firstname)
e.firstname, s.category, s.showname, o.slotdate
FROM shows s
JOIN host e USING (shownumber)
JOIN timeslot o USING (shownumber)
JOIN hostshows h USING (empnum)
ORDER BY e.firstname, o.slotdate DESC;
Detailed explanation:
Select first row in each GROUP BY group?

Sql Server Find Next Most Recent Changed Record

In my employee history table I'm trying to find what the salary was and then what it was changed to. Each Salary change inserts a new record because the new salary is considered a new "job" so it has a start and end date attached. I can select all these dates fine but I keep getting duplicates because I can't seem to compare the current record only against its most recent prior record for that employee. (if that makes sense)
I would like the results to be along the lines of:
Employe Name, OldSalary, NewSalary, ChangeDate(EndDate)
Joe 40,000 42,000 01/10/2011
Example data looks like
EmployeeHistId EmpId Name Salary StartDate EndDate
1 45 Joe 40,000.00 01/05/2011 01/10/2011
2 45 Joe 42,000.00 01/11/2011 NULL
3 46 Bob 20,000.00 01/12/2011 NULL
The Swiss army ROW_NUMBER() to the rescue:
with cte as (
select EmployeeHistId
, EmpId
, Name
, Salary
, StartDate
, EndDate
, row_number () over (
partition by EmpId order by StartDate desc) as StartDateRank
from EmployeeHist)
select n.EmpId
, n.Name
, o.Salary as OldDalary
, n.Salary as NewSalary
, o.EndData as ChangeDate
from cte n
join cte o on o.EmpId = n.EmpId
and n.StartDateRank = 1
and o.StartDateRank = 2;
Use outer join to get employees that never got a raise too.
These kind of queries are always tricky because of data purity issues, if StartDate and EndDate overlap for instance.
I assume the StartDate and EndDate will be same for the new job and previous job.
If thats the case try this.
SELECT a.Name AS EmployeeName, b.Salary AS NewSalary a.Salary AS NewSalary, a.StartDate AS ChangeDate
FROM EMPLOYEE A, EMPLOYEE B
WHERE a.EmpID = b.EmpID
AND a.EndDate IS NULL
AND a.StartDate = b.EndDate
You can use the correlated join operator APPLY which can solve these types of challenges easily
select a.name, curr.salary, prev.salary, prev.enddate
from employee e
cross apply ( -- to get the current
select top(1) *
from emphist h
where e.empid = h.empid -- related to the employee
order by startdate desc) curr
outer apply ( -- to get the prior, if any
select top(1) *
from emphist h
where e.empid = h.empid -- related to the employee
and h.EmployeeHistId <> curr.EmployeeHistId -- prevent curr=prev
order by enddate desc) prev -- last ended