SQL INNER JOIN issue when value as 0 - sql

I have shortened my query & posted only relevant parts. There are many INNER JOINS. Here is the query :-
DECLARE
#startdate DATE = '2018-05-01',
#enddate DATE = '2018-05-05';
WITH calendar
AS (SELECT #startdate AS Dates
UNION ALL
SELECT Dateadd(dd, 1, Dates)
FROM calendar
WHERE Dates < #enddate)
SELECT emp.Code, cv1.CategoryName AS Category FROM (
SELECT *
FROM calendar c
CROSS JOIN (SELECT DISTINCT Id
FROM [dbo].[Employee] where CompanyId = 1) E
WHERE NOT EXISTS (SELECT 1
FROM [dbo].[ShiftSchedule] t
WHERE c.Dates = t.ShiftDate
AND E.Id = t.EmployeeId AND CompanyId = 11)) AS c
INNER JOIN [dbo].[Employee] emp
ON c.Id = emp.Id
INNER JOIN [dbo].[Category] cv1
ON cv1.id = emp.Category
AND emp.Id IN ( 400 )
OPTION (maxrecursion 0)
The problem here is in the INNER JOIN of [dbo].[Category].
In very rare cases - Category is found 0 in Employee Table. So what I want is if Category is 0 then also that record should be included in Result Set. Currently it skips the Records with 0.
Can this be achieved in the above query?
Example :- In Employee table I have an employee Steve & in Category
Column Value is 0. And in Category Table in column Id, there is no
record with 0. So this Employee Steve is not coming in result set.

Basically, you're simply asking for your employee to be returned even when it cannot be matched to a category. That's easy: that's what a LEFT JOIN is for. There's no reason to treat 0 as special here: based on your description, it seems to me as if you'd want the same for any other values that don't exist as category IDs, it's just that there are no such other values.

Related

SQL Return results if one row is not null but still get the other results in that group that are null

I have a query that finds returns results by person but i'm looking to get the results where one of the columns is not null but i still want to see all the other related rows
select person, hiredate, status, mtktype, startdate, enddate, supervisor, termdate, personpercentage
from employees
left join mtkper on person = mtkperson
left join timekeep a on mtkperson = a.tkinit
left join timekeep b on supervisor = b.tkinit
where status <> 'TE'
and mtkdate2 > dateadd(day,datediff(day,0,GetDate())- 0,0)
and person = 'sally'
order by person, status
Returns
But i simply cant do where termdate is not null because then i wont get the other three rows?
One method uses window functions to count the number of non-NULL values:
select x.*
from (select person, hiredate, status, mtktype, startdate, enddate,
supervisor, termdate, personpercentage,
count(termdate) over (partition by person) as cnt_termdate
from employees e left join
mtkper p
on person = mtkperson left join
timekeep tkp
on mtkperson = tkp.tkinit left join
timekeep tks
on supervisor = tks.tkinit
where status <> 'TE' and
mtkdate2 > convert(date, getdate()) and
person = 'sally'
) x
where cnt_person > 0
order by person, status;
I introduced meaningful table aliases. There may be other solutions, but without knowing where the columns comes from, it is hard to make suggestions.

How do i include data from a joined table that doesnt meet the WHERE condition as a null value(SQL)

I have 2 tables employees and Medical leave, which are related by the Employee ID, and basically the Medical leave table will have multiple data of a single employee who takes multiple leaves, I want to filter out the data by the month of the medical leave, and include the employees whose medical leave doesnt occur on the filtered month as a null value.
EMPLOYEES MEDICAL
|employee|ID| |ID|DateOfLeave|
A 1 1 2019/1/3
B 2 1 2019/4/15
C 3 2 2019/5/16
D 4
The sql statement i came up with filters the specific month of the leave and counts the number of times they took a leave on that month such as January, it also includes employees who doesnt have any leave as a '0', however the employees who have medical leaves which doesnt occur on January doesnt show up in the result set at all, how can i show them to have 0 medical leaves in the month of january?
select employees.employee, employees.ID,
count(medical.DateOfLeave) as NumberOfLeaves
from employees
left outer join medical on employees.ID = medical.ID
where (MONTH(DateOfLeave) = 1) or (medical.DateOfLeave is null)
group by employees.employee,employees.ID
RESULT SET
|Employee|ID|NumberOfLeaves|
A 1 1
C 3 0
D 4 0
As you can see B disappears,but i want it to show in the result set as a '0' like employee C and D
I know its because employee B's medical data doesnt meet the condition of the where clause, but how do i write a statement that includes employees who have medical leaves which doesnt occur on january in the result set as a 0??
Your query is only showing results for employees that have leave in January or do not have leave at all.
Instead, you only want to join to records in your medical table if there are records for the specified month, then you'll group your results and get the counts from there.
In order to do this, you need to change the condition for your join to include your Month filter.
Here's a working example using table variables
DECLARE #Employees TABLE (ID INT, Employee CHAR(1))
DECLARE #Medical TABLE (ID INT, DateOfLeave DATE)
INSERT INTO #Employees
VALUES (1, 'A'), (2,'B'), (3,'C'), (4, 'D')
INSERT INTO #Medical
VALUES (1, '2019-01-03'), (1, '2019-04-15'), (2, '2019-05-16')
SELECT e.employee,
e.ID,
count(m.DateOfLeave) AS NumberOfLeaves
FROM #Employees e
LEFT OUTER JOIN #Medical m ON e.ID = m.ID AND MONTH(DateOfLeave) = 1
GROUP BY e.employee,e.ID
left outer join will first join the table and then filter. in your case B is left out in the join itself so thats why the filter is not working. you can try this
select employees.employee, employees.ID,
nvl(count(medical.DateOfLeave),0) as NumberOfLeaves
from employees
left outer join (select * from medical
where (MONTH(DateOfLeave) = 1))
on employees.ID = medical.ID
group by employees.employee,employees.ID
or you can also try this
select e.id, employee, count(dateofleave)
from employee e, medical m
where e.id = m.id
and month(m.DateOfLeave) = 1
group by e.id, employee
UNION ALL
select id, employee, 0
from employee e
where not exists(select 1 from medical m
where e.id = m.id
and month(m.DateOfLeave) = 1

SELECT Statement in CASE

Please don't downgrade this as it is bit complex for me to explain. I'm working on data migration so some of the structures look weird because it was designed by someone like that.
For ex, I have a table Person with PersonID and PersonName as columns. I have duplicates in the table.
I have Details table where I have PersonName stored in a column. This PersonName may or may not exist in the Person table. I need to retrieve PersonID from the matching records otherwise put some hardcode value in PersonID.
I can't write below query because PersonName is duplicated in Person Table, this join doubles the rows if there is a matching record due to join.
SELECT d.Fields, PersonID
FROM Details d
JOIN Person p ON d.PersonName = p.PersonName
The below query works but I don't know how to replace "NULL" with some value I want in place of NULL
SELECT d.Fields, (SELECT TOP 1 PersonID FROM Person where PersonName = d.PersonName )
FROM Details d
So, there are some PersonNames in the Details table which are not existent in Person table. How do I write CASE WHEN in this case?
I tried below but it didn't work
SELECT d.Fields,
CASE WHEN (SELECT TOP 1 PersonID
FROM Person
WHERE PersonName = d.PersonName) = null
THEN 123
ELSE (SELECT TOP 1 PersonID
FROM Person
WHERE PersonName = d.PersonName) END Name
FROM Details d
This query is still showing the same output as 2nd query. Please advise me on this. Let me know, if I'm unclear anywhere. Thanks
well.. I figured I can put ISNULL on top of SELECT to make it work.
SELECT d.Fields,
ISNULL(SELECT TOP 1 p.PersonID
FROM Person p where p.PersonName = d.PersonName, 124) id
FROM Details d
A simple left outer join to pull back all persons with an optional match on the details table should work with a case statement to get your desired result.
SELECT
*
FROM
(
SELECT
Instance=ROW_NUMBER() OVER (PARTITION BY PersonName),
PersonID=CASE WHEN d.PersonName IS NULL THEN 'XXXX' ELSE p.PersonID END,
d.Fields
FROM
Person p
LEFT OUTER JOIN Details d on d.PersonName=p.PersonName
)AS X
WHERE
Instance=1
Ooh goody, a chance to use two LEFT JOINs. The first will list the IDs where they exist, and insert a default otherwise; the second will eliminate the duplicates.
SELECT d.Fields, ISNULL(p1.PersonID, 123)
FROM Details d
LEFT JOIN Person p1 ON d.PersonName = p1.PersonName
LEFT JOIN Person p2 ON p2.PersonName = p1.PersonName
AND p2.PersonID < p1.PersonID
WHERE p2.PersonID IS NULL
You could use common table expressions to build up the missing datasets, i.e. your complete Person table, then join that to your Detail table as follows;
declare #n int;
-- set your default PersonID here;
set #n = 123;
-- Make sure previous SQL statement is terminated with semilcolon for with clause to parse successfully.
-- First build our unique list of names from table Detail.
with cteUniqueDetailPerson
(
[PersonName]
)
as
(
select distinct [PersonName]
from [Details]
)
-- Second get unique Person entries and record the most recent PersonID value as the active Person.
, cteUniquePersonPerson
(
[PersonID]
, [PersonName]
)
as
(
select
max([PersonID]) -- if you wanted the original Person record instead of the last, change this to min.
, [PersonName]
from [Person]
group by [PersonName]
)
-- Third join unique datasets to get the PersonID when there is a match, otherwise use our default id #n.
-- NB, this would also include records when a Person exists with no Detail rows (they are filtered out with the final inner join)
, cteSudoPerson
(
[PersonID]
, [PersonName]
)
as
(
select
coalesce(upp.[PersonID],#n) as [PersonID]
coalesce(upp.[PersonName],udp.[PersonName]) as [PersonName]
from cteUniquePersonPerson upp
full outer join cteUniqueDetailPerson udp
on udp.[PersonName] = p.[PersonName]
)
-- Fourth, join detail to the sudo person table that includes either the original ID or our default ID.
select
d.[Fields]
, sp.[PersonID]
from [Details] d
inner join cteSudoPerson sp
on sp.[PersonName] = d.[PersonName];

sql - find max value

I have a table with 3 columns: Code, Year, percentage.
I need to return the code with the lowest (minimal) percentage in 2009. After this, I want also the name of the code, that exsist in other table that I made.
I only think using CREATE VIEW, but I prefer not to do so.
Select table.code, table.year, table.percentage, othertable.name
from table
inner join othertable on table.FKId = othertable.PKid
where year = 2009
and percentage =
(select min(percentage)
from table
where year = 2009)
Updated to include the othertable... since we don't have names.
UPDATED
Now that we have table names... Updated 3rd time now that I know year is string.
Select E.Code, C.Name
From dbo.Exam E
inner join dbo.Course C
ON E.Code = C.Code
Where E.Year = '2009' and --<-- PROBLEM LIKELY HERE year was string not int.
E.Fail = (select MIN(E2.Fail)
from dbo.Exam E2 where E2.Year = '2009') --<--Don't forget here too.
From comments: sample Data:
INSERT INTO Exam VALUES(333,'2009',40)
INSERT INTO Exam VALUES(333,'2009',20)
INSERT INTO Exam VALUES(555,'2009',19)
INSERT INTO Exam VALUES(444,'2009',19)
INSERT INTO Exam VALUES(777,'2009',23)
INSERT INTO Exam VALUES(333,'2009',0)
INSERT INTO Course VALUES(111,'Name1',5)
INSERT INTO Course VALUES(333,'Name2',5)
INSERT INTO Course VALUES(444,'Name3',6)
INSERT INTO Course VALUES(555,'Name4',3)
INSERT INTO Course VALUES(777,'Name5',3)
INSERT INTO Course VALUES(999,'Name6',6)
Assumption is result should be
Name2, 5
You can use a group by to find the lowest percentage for a year, and then join back to the main tables to find the corresponding other columns:
select *
from CodeYearPercTbl cyp
join CodeTbl c
on c.Code = cyp.Code
join (
select Year
, min(Percentage) as MinPerc
from CodeYearPercTbl
group by
Year
) as filter
on filter.Year = cyp.Year
and filter.MinPerc = cyp.Percentage
where cyp.Year = 2009
Table-1 : Code,Year,Percentage
Table-2 : Code,CodeName
select T1.Code,T2.CodeName,T1.Percentage from
(
select TOP 1 Code,Percentage
from Table-1
where Year = '2009'
order by Percentage asc
) T1 inner join Table-2 T2 on T1.Code = T2.Code
Try this:
select a.Code, c.Name
from YourTable a inner join AnotherTable c on a.Code = c.Code
where a.Percentage = (select MIN(Percentage)
from YourTable b where b.Year = '2009'
)

Select rows depending on field value

I have 2 tables: Employee and Appointments.
1 Employee can have multiple Appointments.
There is a field: Visibility which can hold 0, 1 or 2.
0: show any of the appointment,
1: Show this appointment only,
2: Don't show the appointment.
Now i want to select the records of employee and appointments:
if visibility is 1 then select that record and not any other records
if visibility is 0 then select just a record, like the top 1
if visibility is 2 then select nulls for that record, except the employee id.
can anyone point me out how it can be done using Sql Server 2000?
I'm making a couple of assumptions.
Its possible to have more than one appointment
If you have an appointments with 1 and 0 you want to Guarantee the one with 1 is shown
You can only have one record with 1
SELECT
e.empid,
COALESCE(OneAppoint.appointmentID, ZeroAppoint.appointmentID) appointmentID
FROM
employee e
LEFT JOIN appointment ZeroAppoint
INNER JOIN (
SELECT
max(a.appointmentid) appointmentid ,
a.EmpID
FROM
appointment a
WHERE
a.Visibility =0
) maxZeroAppoint
ON ZeroAppoint.appointmentid = maxZeroAppoint.appointmentid
ON e.empID = ZeroAppoint.empID
LEFT JOIN appointment OneAppoint
ON e.empID = OneAppoint.empID
and OneAppoint.Visibility = 1
With SelectedAppointments AS
{
SELECT * FROM Appointments WHERE Visibility <> 2
}
SELECT Employee.Id, < All columns of SelectedAppointments unless EmployeeId>
FROM Employee LEFT JOIN SelectedAppointments
on Employee.Id = SelectedAppointments.EmployeeId
select *
from Employee as Emp
left outer join
(select top 1 *
from Appointments
where Visibility <> 2
order by Visibility desc) as App
on Emp.EmpID = App.EmpID