How to determine two different aggregate dates on employee attendance data? - sql

I Need help to implement employee attendance sheet. Presently am having employee attendance i.e
Query:
SELECT c.First_name + c.Middle_name + c.last_name AS employeename,
b.Device_Person_id,a.Dept_Id,Date1,
CASE WHEN b.Device_Person_id IS NOT NULL
THEN 'P'
ELSE 'A' END AS status
FROM Emp_setting a
LEFT OUTER JOIN (SELECT Device_Person_id, MAX(logDateTime) AS Date1
FROM tempDeviceLogs
GROUP BY Device_Person_id) b
ON a.personal_id = b.Device_Person_id
LEFT OUTER JOIN persons_profile c
ON c.pesonal_id=a.personal_id
Result:
employeename Device_person_id dept_id date1 status
MEHABOOB NULL 4 NULL A
UDAY NULL 26 NULL A
SHANKRAYYA NULL 10 NULL A
BASAVARAJ NULL 24 NULL A
BHIMAPPA 5 10 2014-05-23 14:14:00.000 P
i.e. Employeename BHIMAPPA is present on 2014-05-23.
NOW I want the list of employees who is present on 2014-05-23.
Please help?

Assuming that logDateTime indicates the presence of the employee, and given that the current derived table returns the MAX() of this field (presumably the last time the employee was detected) you would need another join to the tempDeviceLogs table to do the filtering. You could do the filtering at the same time with an INNER JOIN, viz:
...
INNER JOIN (SELECT Device_Person_id
FROM tempDeviceLogs
WHERE logDateTime >= '2014-05-23' and logDateTime < '2014-05-24') x
ON x.Device_Person_id = a.personal_id
Edit
Given that you want to SELECT the date as well, I'm assuming you want to parameterize it / use it for a range. And making yet another assumption about your SqlServer version being >= 2008, cast the DateTime to a Date and group by it:
SELECT c.First_name + c.Middle_name + c.last_name AS employeename,
b.Device_Person_id,a.Dept_Id, Date1 as DateTimeLastSeen,
x.logDate As DatePresent,
CASE WHEN b.Device_Person_id IS NOT NULL THEN 'P' ELSE 'A' END AS status
FROM Emp_setting a
LEFT OUTER JOIN (SELECT Device_Person_id, MAX(logDateTime) AS Date1
FROM tempDeviceLogs
GROUP BY Device_Person_id) b
ON a.personal_id = b.Device_Person_id
LEFT OUTER JOIN persons_profile c
ON c.pesonal_id=a.personal_id
INNER JOIN (SELECT Device_Person_id, CAST(logDateTime AS DATE) as logDate
FROM tempDeviceLogs
GROUP BY Device_Person_id, CAST(logDateTime AS DATE)) x
ON x.Device_Person_id = a.personal_id
WHERE
logDate BETWEEN '2014-05-01' AND '2014-05-23';
This could also be done with a DISTINCT. If you have an earlier version of SqlServer, use a hack like this to obtain the date part of a DateTime (in the select + group)

Create a variable to hold the date which you are looking for.
DECLARE #cutOffDate DATE
SET #cutOffDate = '23-05-2014'
Then add to the end of your statement
Where Date1 = #cutOffDate

Related

SQL statement return matching records from join and null records from join

I have a db with records like the following structure in the table;
id,msg,date,fid
19 ,"start","2012-02",NULL
20 ,"end","2012-03",19
21 , "start",2012-04,NULL
I have and entire db set up like this where the fid in some cases matches the id of another record. Id like to return all records that have matching start and end dates and just the start date if it doesn't have a corresponding end date fid like below:
19 ,"start","2012-02","end","2012-03"
21 , "start,"2012-04,"",NULL
I think you want a left join:
select t.fid, t.date as start_date, tf.date as end_date
from t left join
t tf
on tf.id = t.fid
where t.msg = 'start'
This assumes that there are only two types of records and fid is always non-NULL for 'end' records.
You just need a LEFT OUTER JOIN to your table. You can use an alias to refer to each instance:
SELECT startdt.id, startdt.msg, startdt.date, enddt.msg, enddt.date
FROM yourtable as startdt
LEFT OUTER JOIN yourtable as enddt
ON startdt.fid = enddt.id
AND enddt.msg = 'end'
WHERE startdt.msg = 'start'

How to join two tables having two common column values and unioning the rest

I'd like to combine Table A and Table B at the link below and end up with Table C. What is the best way to do this in SQL? I've thought about creating a composite key between the tables for LedgerID + Year doing an inner join and then unioning the left and right only data. I'm also curious how to avoid duplicating values across rows like Balance = 50.00 ending up in rows for Tires and Windshield.
Try a full outer join, joining on LedgerID and Year, using coalesce to show Table B's LedgerID/Year when Table A's is NULL:
SELECT
COALESCE(A.LedgerID, B.LedgerID) as LedgerID,
COALESCE(A.Year, B.Year) as Year,
A.Title,
A.Payment,
B.Balance
FROM "Table A" AS A
FULL OUTER JOIN "Table B" AS B ON (A.LedgerID=B.LedgerID AND A.Year=B.Year)
--Please try this Query. Since you have only reference LedgerId and Year, the balance will show 50 for both Tires & Windshield
; with cte_Ledger (LedgerId, [year])
AS
(
Select DISTINCT LedgerId, [year]
From tableA
UNION
Select DISTINCT LedgerId, [year]
From tableB
)
select t.LedgerId
, t.[year]
, t1.Title
, T1.Payments
, t2.Balance
FROM cte_Ledger t
left join tableA t1 on t.LedgerId = t1.LedgerId and t.[year] = t1.[year]
left join tableB t2 on t2.LedgerId = t.LedgerId and t2.[year] = t.[year]
I think so, Above Queries will not help to get expected result.
some misunderstanding is with requirement.
For ledgerid = 22 and Year = 2017, have 2 records in table-A and 1 with Table-B. But in expecting result, Balance 50(Record of table-B) is exists with matched first row of Table-A only. As per above all logic it will be with 2 Records where ledgerid = 22, Year = 2017 and Title with "Tires" & "Windshield".
If required same result as mentioned then need to use recursive CTE or ranking function with order of ID column.
Here is my solution after I loaded the tables, another nested case statement may be need to format out the zero on Ledger 24.
Select
[LedgerID],
[Year],
Case when PayRank = 1 then Title else '' end as Title,
Case when PayRank = 1 then convert(varchar(20),Payments) else '' end as
Payments,
Case when BalRank = 1 then convert(varchar(20),Balance) else '' end as
Balance
from(
SELECT
B.[LedgerID]
,B.[Year]
,Rank()Over(Partition by B.LedgerID,Payments order by
B.LedgerID,B.Year,Title) as PayRank
,isnull([Title],'') as Title
,isnull([Payments],0) as Payments
,Rank()Over(Partition by B.LedgerID,B.Year order by
B.LedgerID,B.Year,Payments) as BalRank
,Balance
FROM [TableB] B
left outer join [TableA] A
on A.LedgerID = B.LedgerID
) Query
order by LedgerID,Year

Insert data in ssrs table in the query

I would like to insert some data in the ssrs table.
I would like to show it like this here:
How can I add these data in my query in SSRS. I have no possibility to change something in the database.
| P1|P2 |P3 |P4 |P5 |P6 |P7 |P8
Group A|84%|87%|81%|81%|79%|96%|86%|88%
Group B|66%|22%|79%|64%|53%|94%|5% |23%
The Problem is:
Last week on wednesday the database did not recorded the data from Group A and Group B. And I have no possibility to correct/add the missing data in the database. And thats why I would like to add these missed data in my query and show it in the report.
My query:
SELECT *
FROM (
Select
intervaldate as Datum
,tsystem.Name as Name
,team as group
,SUM(GoodUnits) As Goods
,SUM(TheoreticalUnits) As Units
from tCount inner join tsystem ON tCount.systemid = tsystem.id
where IntervalDate >= #StartDateTime AND IntervalDate <= #EndDateTime
group by intervaldate
) c
inner join
(
SELECT
sh.Date as Datum,
sc.Name as Name
FROM tHistory sh
INNER JOIN tSchedule sc ON (sc.ID = sh.ScheduleID)
WHERE Scheduled != 0
) p ON p.Name = c.Name
When I realized that the data was not recorded I did written down the data on paper.
To add manual data to your posted query, you can use UNION ALL and VALUES like so:
First make sure you get your 'additional data' correct on its own. Try this example:
SELECT Datum,Name,[Group],Goods,Units
FROM (
VALUES
(CAST('2015-01-01' AS DATE),'AName','A',10.32,20.76),
(CAST('2015-01-01' AS DATE),'AName','B',12.72,16.15)
) AS ExtraData(Datum,Name,[Group],Goods,Units);
I am making many assumptions here as you have not provided enough info in your question.
Anyway if that is correct, then you simply attach it to your original data with UNION ALL
SELECT Datum,Name,[Group],Goods,Units
FROM (
Select
intervaldate as Datum
,tsystem.Name as Name
,team as [Group]
,SUM(GoodUnits) As Goods
,SUM(TheoreticalUnits) As Units
from tCount inner join tsystem ON tCount.systemid = tsystem.id
where IntervalDate >= #StartDateTime AND IntervalDate <= #EndDateTime
group by intervaldate
) c
inner join
(
SELECT
sh.Date as Datum,
sc.Name as Name
FROM tHistory sh
INNER JOIN tSchedule sc ON (sc.ID = sh.ScheduleID)
WHERE Scheduled != 0
) p ON p.Name = c.Name
/* Original query ends. Now add more data */
UNION ALL
SELECT Datum,Name,[Group],Goods,Units
FROM (
VALUES
(CAST('2015-01-01' AS DATE),'AName','A',10.32,20.76),
(CAST('2015-01-01' AS DATE),'AName','B',12.72,16.15)
) AS ExtraData(Datum,Name,[Group],Goods,Units);

SQL Query For Customers Not Used For Last 3 Years

I THINK I'm having some trouble.
I'm trying to query 2 tables for customers that haven't been used on the table for the last 3 Years. The data consists of data ranging for 7+ years, so customers are used multiple times.
I think the issue with my current query: It's finding data of customers not used in the last 3 years... but it's not accounting for if there is also data of the customer within the last 3 years as well.
Can someone possibly help me? I'm guessing the answer is to use only the data of the customer with the latest date and ignore previous data.
SELECT DISTINCT
tbl_Customer.CustomerID
, tbl_Customer.CustomerName
, Table1.ImportDate
, Table2.ImportDate
FROM
tbl_Customer
LEFT JOIN
Table1 ON tbl_Customer.CustomerName = Table1.CustomerName
LEFT JOIN
Table2 ON tbl_Customer.CustomerName = Table2.CustomerName
WHERE
(((DateAdd("yyyy", 3, [Table2].[ImportDate])) < Now())
AND
((DateAdd("yyyy", 3, [Table1].[ImportDate])) < Now()))
ORDER BY
Table1.ImportDate DESC,
Table2.ImportDate DESC;
The core problem with the initial query is that, for no imports (which will happen for "no order" customers) the condition
DateAdd("yyyy", 3, ImportDate) < Now()
--> DateAdd("yyyy", 3, NULL) < Now()
--> NULL < Now()
--> NULL (or not true)
is not true. A simple fix is to add a guard
([Table1].[ImportDate] IS NULL
OR DateAdd("yyyy", 3, [Table1].[ImportDate]) < Now())
around such expressions or to coalesce the NULL value before using it.
The ordering will also be wrong, as that means order by one value and then the other, not "by the greater of both" values. Compare with
ORDER BY
IIF(Table1.ImportDate > Table2.ImportDate, Table1.ImportDate, Table2.ImportDate)
However, I would use a LEFT JOIN on customers/orders, GROUP BY with a MAX on the order dates. Then you can use that result (as a derived subquery) to complete the query asked fairly trivially.
SELECT
c.CustomerID
, MAX(o.ImportDate) as lastImport
FROM tbl_Customer as c
-- The UNION is to simply "normalize" to a single table.
-- (Also, shouldn't the join be on a customer "ID"?)
LEFT JOIN (
SELECT CustomerName, ImportDate from Table1
UNION
SELECT CustomerName, ImportDate from Table2) as o
ON c.CustomerName = o.CustomerName
GROUP BY c.CustomerID
Then,
SELECT s.CustomerID
FROM (thatSubQuery) as s
WHERE
-- no orders
s.lastImport IS NULL
-- only old orders
OR DateAdd("yyyy", 3, s.lastImport) < Now()
ORDER BY s.lastImport
(YMMV with MS Access, this will work in a "real" database ;-)
SELECT DISTINCT
tbl_Customer.CustomerID,
tbl_Customer.CustomerName,
Table1.ImportDate,
Table2.ImportDate
FROM (tbl_Customer
LEFT JOIN Table1
ON tbl_Customer.CustomerName = Table1.CustomerName)
LEFT JOIN Table2
ON tbl_Customer.CustomerName = Table2.CustomerName
WHERE DateAdd("yyyy",3,[Table2].[ImportDate]) < Now()
AND DateAdd("yyyy",3,[Table1].[ImportDate]) < Now()
AND tbl_Customer.CustomerID NOT IN (
SELECT DISTINCT
tbl_Customer.CustomerID,
FROM (tbl_Customer
LEFT JOIN Table1
ON tbl_Customer.CustomerName = Table1.CustomerName)
LEFT JOIN Table2
ON tbl_Customer.CustomerName = Table2.CustomerName
WHERE DateAdd("yyyy",3,[Table2].[ImportDate]) >= Now()
AND DateAdd("yyyy",3,[Table1].[ImportDate]) >= Now()
)
ORDER BY Table1.ImportDate DESC , Table2.ImportDate DESC;
Based on what I can infer from your query about your data structure, I think you want something like this:
DECLARE #CutOff DateTime
SET #CutOff = DATEADD(y, -3 GETDATE())
SELECT tbl_Customer.CustomerID, tbl_Customer.CustomerName
WHERE (CustomerName IN
(SELECT CustomerName FROM Table1 WHERE ImportDate < #CutOff))
OR
(CustomerName IN
(SELECT CustomerName FROM Table2 WHERE ImportDate < #CutOff)))
AND CustomerName NOT IN
(SELECT CustomerName FROM Table1 WHERE ImportDate > #CutOff)
AND CustomerName NOT IN
(SELECT CustomerName FROM Table2 WHERE ImportDate > #CutOff)

How do you handle NULLs in a DATEDIFF comparison?

I have to compare 2 separate columns to come up with the most recent date between them. I am using DATEDIFF(minute, date1, date2) to compare them, however, in some records the date is Null, which returns a null result and messes up the CASE.
Is there a way around this, or a way to predetermine which date is null up front?
(psudocode)
UPDATE TABLE
SET NAME = p.name,
NEW_DATE = CASE WHEN DATEDIFF(minute,d.date1,d.date2) <= 0 THEN d.date
ELSE d.date2
END
FROM TABLE2 d
INNER JOIN TABLE3 p
ON d.ACCTNUM = p.ACCTNUM
You can just add extra logic into your CASE:
UPDATE TABLE
SET NAME = p.name,
NEW_DATE = CASE
WHEN d.date1 IS NULL THEN -- somewhat
WHEN d.date2 IS NULL THEN -- somewhat
WHEN DATEDIFF(minute,d.date1,d.date2) <= 0 THEN d.date
ELSE d.date2
END
FROM TABLE2 d
INNER JOIN TABLE3 p
ON d.ACCTNUM = p.ACCTNUM
I would use ISNULL.
UPDATE TABLE
SET NAME = p.name,
NEW_DATE = CASE WHEN ISNULL(DATEDIFF(minute,d.date1,d.date2), 0) <= 0 THEN d.date
ELSE d.date2
END
FROM TABLE2 d
INNER JOIN TABLE3 p
ON d.ACCTNUM = p.ACCTNUM
or maybe
ISNULL(DATEDIFF(minute,d.date1,d.date2), 1)
if you want to handle the null values the other way around.
You can try some think like this. You can use Is Null to check null.
UPDATE TABLE
SET NAME = p.name,
NEW_DATE = CASE Case When date2 Is Null Then GetDate()
Case When date1 Is Null Then GetDate()
WHEN DATEDIFF(minute,d.date1,d.date2) <= 0 THEN d.date
ELSE d.date2
END
FROM TABLE2 d
INNER JOIN TABLE3 p
ON d.ACCTNUM = p.ACCTNUM
Microsoft's ISNULL() function is used to specify how we want to treat
NULL values.
In this case we want NULL values to be zero.
Below, if "UnitsOnOrder" is NULL it will not harm the calculation,
because ISNULL() returns a zero if the value is NULL:
SQL Server / MS Access
SELECT ProductName,UnitPrice*(UnitsInStock+ISNULL(UnitsOnOrder,0)) FROM Products
SQL NULL Functions
This should give you what you want with minimal processing required.
UPDATE TABLE
SET NAME = p.name,
NEW_DATE = CASE WHEN COALESCE(date1, date2)>COALESCE(date2, date1)
THEN COALESCE(date1, date2)
ELSE COALESCE(date2, date1)
END
FROM TABLE2 d
INNER JOIN TABLE3 p
ON d.ACCTNUM = p.ACCTNUM
WHERE NOT (date1 is null and date2 is null);
I personally use this:
IIF([yourvariable] is null, expression_when_true, expression_when_false)
This actually works well if you have more complex datatypes like DATE. I've looked for the solution also and lots of people asked how to substract null date or why simple 0000-00-00 doesn't work. The IIF can avoid substraction when minuend or subtrahend is null.
Example:
IIF([ReturnDate] is null,
datediff(day,[BookDate],getdate())*CostPerDay,
datediff(day,[BookDate],[ReturnDate])*CostPerDay)