Mismatched MySQL dates evaluate to equal - mysql

I have this query:
select w.EventName, w.EventLocation, CONCAT(CURDATE(), ' ', w.RecurringEventTime) AS RecurringEventTime, w.OneTimeDateTime
from EventClickIns eci
join WebEvents w
on eci.WebEventID = w.ID
where eci.UserID = 493
and eci.WebEventID <> 10
and eci.InvitationID <> 175
and date(eci.ClickInDate) = date(now())
and (RecurringEventTime = '2018-03-19 12:00:00' OR w.OneTimeDateTime = '2018-03-19 12:00:00')
limit 1
And I get this record as the result:
EventName EventLocation RecurringEventTime OneTimeDateTime
========================================================================
Evt ABC 123 Anystreet 2018-03-20 12:00:00 NULL
It's baffling why I am getting a matching record when the RecurringEventTime in my where clause is different from the RecurringEventTime in the matching record. OneTimeDateTime is null, so that cannot be matching.
What am I missing?

Thanks to hints from Sam and Solarflare, I got it figured out:
select w.EventName, w.EventLocation, CONCAT(CURDATE(), ' ', w.RecurringEventTime) AS RecurringEventTime, w.OneTimeDateTime
from EventClickIns eci
join WebEvents w
on eci.WebEventID = w.ID
where eci.UserID = 493
and eci.WebEventID <> 10
and eci.InvitationID <> 175
and date(eci.ClickInDate) = date(now())
and (CONCAT(CURDATE(), ' ', w.RecurringEventTime) = '2018-03-19 12:00:00' OR w.OneTimeDateTime = '2018-03-19 12:00:00')
limit 1

Related

Converting MySQL Queries to SQL Server

Can anyone please help converting the MySQL query below to SQL Server.
The 'DATE_FORMAT' and the LIMIT:limit are the problem.
SELECT
DATE_FORMAT(e.eventTime, '%m/%d/%Y %h:%i:%s %p') eventTime,
e.displayPath,
SUBSTRING_INDEX(source, '/alm:', -1) name,
e.eventType,
CASE WHEN e.priority = 0 THEN 'Diagnostic' WHEN e.priority = 1 THEN 'Low' WHEN e.priority = 2 THEN 'Medium' WHEN e.priority = 3 THEN 'High' WHEN e.priority = 4 THEN 'Critical' ELSE '' END priority,
COALESCE(COALESCE(COALESCE(d.intvalue, d.floatvalue), d.strvalue), '') eventValue,
COALESCE(ack.strvalue, '') ackUser
FROM
alarm_events e
LEFT JOIN alarm_event_data d ON d.id = e.id AND d.propname = 'eventValue'
LEFT JOIN alarm_event_data ack ON ack.id = e.id AND ack.propname = 'ackUser'
WHERE
eventtime BETWEEN :startDate AND :endDate AND priority BETWEEN :minPriority AND :maxPriority AND
((:active AND e.eventtype = 0) OR (:clear AND e.eventtype = 1) OR (:ack AND e.eventtype = 2))
ORDER BY
e.eventTime DESC
LIMIT :limit
Variables in SQL use an # instead of the : so you would need to adjust for that and populate your variables from the application.
Instead of the LIMIT parameter, you'd begin the query with a SELECT TOP #limit
The SUBSTRING_INDEX function needs to be adjusted to a standard SUBSTRING and PATINDEX function.
You would need to use the FORMAT() instead of DATE_FORMAT() function ().
As mentioned in the comments, using additional COALESCE() functions in SQL Server is unnecessary.
So the final query would look something like this (with variables to be set by your calling program):
SELECT TOP (#limit)
FORMAT(e.eventTime, 'M/d/yy hh:mm:ss tt') eventTime,
e.displayPath,
SUBSTRING(source, patindex(source,'/alm:'),len(source)) name,
e.eventType,
CASE WHEN e.priority = 0 THEN 'Diagnostic' WHEN e.priority = 1 THEN 'Low' WHEN e.priority = 2 THEN 'Medium' WHEN e.priority = 3 THEN 'High' WHEN e.priority = 4 THEN 'Critical' ELSE '' END priority,
COALESCE(d.intvalue, d.floatvalue, d.strvalue, '') eventValue,
COALESCE(ack.strvalue, '') ackUser
FROM
alarm_events e
LEFT JOIN alarm_event_data d ON d.id = e.id AND d.propname = 'eventValue'
LEFT JOIN alarm_event_data ack ON ack.id = e.id AND ack.propname = 'ackUser'
WHERE
eventtime BETWEEN #startDate AND #endDate AND priority BETWEEN #minPriority AND #maxPriority AND
((#active AND e.eventtype = 0) OR (#clear AND e.eventtype = 1) OR (#ack AND e.eventtype = 2))
ORDER BY
e.eventTime DESC

Workaround for Subquery returns more than 1 error

Please help me out.
I'm creating a query to list down all the Principal (SLE_CODE = 11) and Interest (SLE_CODE = 23) Payment made by the client's loan (REF_NO field) in a columnar way.
I tried this code, but it returns an error of "subquery returns more than 1 row".
SELECT DATE_FORMAT(TR_DATE, '%M %d, %Y'), SL_CLIENTID, REF_NO, SLE_CODE, (select amt from sldtl where REF_NO = 1958
AND SL_CLIENTID = 1782 AND SLC_CODE = 12 AND SLE_CODE = 23) as interest,
(select amt from sldtl sd where REF_NO = 1958
AND SL_CLIENTID = 1782 AND SLC_CODE = 12 AND SLE_CODE = 11) as principal
FROM sldtl
WHERE REF_NO = 1958
AND SL_CLIENTID = 1782 AND SLC_CODE = 12
Please refer to this sqlfiddle:
http://sqlfiddle.com/#!9/3ac32/2
Thank you in advance.
Sample data and desired results would really help. I'm pretty sure you want conditional aggregation. Perhaps this:
SELECT DATE_FORMAT(TR_DATE, '%M %d, %Y'),
SL_CLIENTID, REF_NO,
SUM(CASE WHEN SLE_CODE = 23 THEN amt END) as interest,
SUM(CASE WHEN SLE_CODE = 11 THEN amt END) as principal
FROM sldtl
WHERE REF_NO = 1958 AND SL_CLIENTID = 1782 AND SLC_CODE = 12
GROUP BY DATE_FORMAT(TR_DATE, '%M %d, %Y'),
SL_CLIENTID, REF_NO;
You need a case.. when expression :
SELECT DATE_FORMAT(TR_DATE, '%M %d, %Y'),
SL_CLIENTID, REF_NO, SLE_CODE,
case when SLE_CODE = 23 then AMT else null end as interest,
case when SLE_CODE = 11 then AMT else null end as principal
FROM sldtl
WHERE REF_NO = 1958
AND SL_CLIENTID = 1782
AND SLC_CODE = 12;
Demo

Combining two mysql query returns ok instead of rows

I have a query in which I return some information regarding an invoice, I take that invoice and compare it to another table "payment" to see if that invoice (fullamount -fullpaid) exists in the other table and if it does some function should not run in my backend code.
SELECT a.status, a.rf_reference, a.payor_orig_id , a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null
having fullamount > fullpaid
order by a.ORIG_ID;
The above query returns
status| rf_reference | payor_orig_id | fullamount | fullpaid
30 RF123456 212 1000 200
So now I take the above information and pass it onto another query to see if a row field matches.
I pass it on like this
select *
from payment
where
payor_orig_id = 212 and
rf_reference = RF123456 and
payment_amount = (1000-200) and
status = 10 and
INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
UPDATE_DT IS NULL;
So now the above code will return a row by which basically I do not run my backend function.
Since this are two separate query I would like to combine them to one where I make sure that I add a having statement and check that ONLY rows are returned where there is no match between the invoice and payment table.
SELECT a.status, a.rf_reference, a.payor_orig_id , a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid,
(select b.payment_amount
from payment b
where
b.payor_orig_id = a.payor_orig_id and
b.rf_reference = a.rf_reference and
b.status = 10 and
b.INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
b.UPDATE_DT IS NULL) as payment_amount
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null
having fullamount > fullpaid and
(fullamount - fullpaid ) <> payment_amount
order by a.ORIG_ID;
The above query returns "OK" which is odd since I am not sure how to debug it.
Try seeing if the other table exists or not using NOT EXIST
SELECT a.* ,
a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null and
NOT EXISTS ( select *
from payment
where
payor_orig_id = a.payor_orig_id and
rf_reference = a.rf_reference and
payment_amount = ((a.gross_amount + a.type_related_amount) - (a.gross_paid + a.type_related_paid)) and
status = 10 and
INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
UPDATE_DT IS NULL )
having fullamount > fullpaid
order by a.ORIG_ID;

Calculate two records and join them in the same MySQL row

I have a table that contains the data of an access control of employees, entry and exits from the office in different rows.
The output is based on a query in a date and time range example
ac_date >= '2018-05-12' AND ac_date <= '2018-05-13' AND ac_time >='08:00:00' AND ac_time <= '13:00:00']
Table AC
CREATE TABLE `AC` (
`employee` int(11) NOT NULL,
`ac_date` date NOT NULL,
`ac_time` time NOT NULL,
`ac_event` tinyint(4) NOT NULL,
KEY `index2` (`employee`,`ac_date`,`ac_time`,`ac_event`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Employee ac_date ac_time ac_event
2032 2018-05-12 08:52:00 1
2032 2018-05-12 11:39:33 0
2032 2018-05-12 11:48:06 1
2032 2018-05-12 11:52:54 0
2032 2018-05-12 11:59:54 1
2032 2018-05-12 12:23:40 0
2032 2018-05-13 08:34:43 1
2032 2018-05-13 09:02:25 0
2032 2018-05-13 09:12:16 1
2032 2018-05-13 11:45:21 0
2032 2018-05-13 12:50:40 1
2032 2018-05-13 12:52:16 0
Where ac_ event = 1 is the input and ac event = 0 is the output.
I need to make a query that shows the data in the same row (Entry / Exit) to then take the time the employee lasts outside the office.
Ej:
Output for the first block:
Employee entry_date exit_date entry_date exit_date duration
2032 2018-05-12 2018-05-12 08:52:00 11:39:33 02:47:33
I was able to get the result by doing a query with the same table, but I have duplicate data, so I have to resort to the group by [ac_date, ac_time]. I do not know if the way I'm trying is the right one, so I'd like to see an expert's solution. Thank you!
Update:
http://sqlfiddle.com/#!9/6f36f/3
This is a simplified version of your original query:
select AC.employee, AC.ac_date, AC.ac_time,
min(AC2.ac_time) as exit_time,
timediff(min(AC2.ac_time), AC.ac_time)
from AC
left join AC as AC2
on AC2.ac_date = AC.ac_date
and AC2.ac_time > ac.ac_time
and AC2.ac_event = 0
where AC.ac_event = 1
AND AC.ac_date >= '2018-05-11'
AND AC.ac_date <= '2018-05-13'
AND AC.ac_time >= '00:00:00'
AND AC.ac_time <= '23:59:00'
group by AC.employee, AC.ac_date, AC.ac_time
order by AC.employee, AC.ac_date, AC.ac_time
;
See fiddle
But no matter how you write it, it's always a kind of non-equi-join (i.e. not based on =) and performance might be bad without matching indexes.
Btw, if this was MariaDB or MySQL 8 it would be a simple task for LAG/LEAD.
Try this:
SELECT
*,
TIMEDIFF(exit_time,entry_time) as duration
FROM
(
SELECT
Employee,
ac_date as entry_date,
ac_date as exit_date,
MIN(CASE WHEN ac_event=1 THEN ac_time END) entry_time,
MAX(CASE WHEN ac_event=0 THEN ac_time END) exit_time
FROM
AC
GROUP BY
Employee,
ac_date
) as t
You can use correlated subqueries to get entry / exit pairs:
SELECT employee, ac_date AS entry_date,
(SELECT ac_date
FROM AC AS i
WHERE i.ac_event = 0
AND i.employee = t.employee
AND i.ac_date >= t.ac_date
AND i.ac_time >= t.ac_time
ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_date,
ac_time AS entry_time,
(SELECT ac_time
FROM AC AS i
WHERE i.ac_event = 0
AND i.employee = t.employee
AND i.ac_date >= t.ac_date
AND i.ac_time >= t.ac_time
ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_time
FROM AC AS t
WHERE t.ac_date BETWEEN '2018-05-12' AND '2018-05-13' AND t.ac_event = 1;
Output:
employee entry_date exit_date entry_time exit_time
-------------------------------------------------------
2032 2018-05-12 2018-05-12 08:52:00 11:39:33
2032 2018-05-12 2018-05-12 11:48:06 11:52:54
2032 2018-05-12 2018-05-12 11:59:54 12:23:40
2032 2018-05-13 2018-05-13 08:34:43 09:02:25
2032 2018-05-13 2018-05-13 09:12:16 11:45:21
2032 2018-05-13 2018-05-13 12:50:40 12:52:16
Then you can use TIMESTAMPDIFF to calculate the time between each entry - exit:
SELECT employee, entry_date, exit_date, entry_time, exit_time,
SEC_TO_TIME(TIMESTAMPDIFF(SECOND,
CONCAT(entry_date, ' ', entry_time),
CONCAT(exit_date, ' ', exit_time))) AS duration
FROM (
SELECT employee, ac_date AS entry_date,
(SELECT ac_date
FROM AC AS i
WHERE i.ac_event = 0
AND i.employee = t.employee
AND i.ac_date >= t.ac_date
AND i.ac_time >= t.ac_time
ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_date,
ac_time AS entry_time,
(SELECT ac_time
FROM AC AS i
WHERE i.ac_event = 0
AND i.employee = t.employee
AND i.ac_date >= t.ac_date
AND i.ac_time >= t.ac_time
ORDER BY i.ac_date, i.ac_time LIMIT 1) AS exit_time
FROM AC AS t
WHERE t.ac_date BETWEEN '2018-05-12' AND '2018-05-13' AND t.ac_event = 1) AS x
Demo here
TRY THIS: Just rank data for every entry employee, ac_date, ac_time wise and exit in the separate set then join, it's the one simple way where you can use INNER JOIN instead of LEFT JOIN for the better performance if you want the records with both entry and exit
SELECT en.employee,
en.entry_date,
ex.exit_date,
en.entry_time,
ex.exit_time,
TIMEDIFF(ex.exit_time, en.entry_time) as duration
FROM (
SELECT employee,
ac_date AS entry_date,
ac_time AS entry_time,
#enRank := #enRank + 1 AS rank
FROM AC, (SELECT #enRank := 0) r
WHERE ac_event = 1 ORDER BY employee, ac_date, ac_time) en
LEFT JOIN (
SELECT employee,
ac_date AS exit_date,
ac_time AS exit_time,
#exRank := #exRank + 1 AS rank
FROM AC, (SELECT #exRank := 0) r
WHERE ac_event = 0 ORDER BY employee, ac_date, ac_time) ex ON ex.rank = en.rank
AND ex.employee = en.employee
WHERE en.entry_date >= '2018-05-12' AND ex.exit_date <= '2018-05-13'
ORDER BY en.entry_date
OUTPUT http://sqlfiddle.com/#!9/891d91/18
Another option is to use a double join to the same table in order to get the exit record:
SELECT t1.employee,
t1.ac_date AS entry_date,
t1.ac_time AS entry_time,
t2.ac_date AS exit_date,
t2.ac_time AS exit_time,
SEC_TO_TIME(TIMESTAMPDIFF(SECOND,
CONCAT(t1.ac_date, ' ', t1.ac_time),
CONCAT(t2.ac_date, ' ', t2.ac_time))) AS duration
/* fetches entry records */
FROM AC AS t1
/* fetches exit records that occur after the correlated entry record */
INNER JOIN AC AS t2
ON t1.employee = t2.employee AND t2.ac_event = 0
AND CONCAT(t1.ac_date, ' ', t1.ac_time) <= CONCAT(t2.ac_date, ' ', t2.ac_time)
/* fetches exit records that occur between t1, t2 records */
LEFT JOIN AC AS t3
ON t2.employee = t3.employee AND t3.ac_event = 0
AND CONCAT(t3.ac_date, ' ', t3.ac_time) >= CONCAT(t1.ac_date, ' ', t1.ac_time)
AND CONCAT(t3.ac_date, ' ', t3.ac_time) < CONCAT(t2.ac_date, ' ', t2.ac_time)
WHERE t1.ac_date BETWEEN '2018-05-12' AND '2018-05-13'
AND t1.ac_event = 1
AND t3.employee IS NULL /* There is no record between t1 entry and t2 exit */
Demo here

Concatenating row values sql server 2008 r2

I have two tables register and att_bottom and I want to display only the students at a certain building who have been tardy based on today's date with the periods separated by a comma.
This is the way the data is displayed when joining both tables:
Student ID | Building | Period | Grade
12345 2 1 11
12345 2 5 11
43210 2 1 12
I want this:
Student ID | <u>Building | Period | Grade
12345 2 1,5 11
43210 2 1 12
This is my query:
select r.STUDENT_ID,
r.BUILDING ,
(select ab.attendancePeriod + ','
from att_bottom ab
where ab.STUDENT_ID = r.student_id
and ab.building = '2'
and ab.attendance_c ='T'
and ab.SCHOOL_YEAR =2014
CONVERT(date,ab.attendance_date,102) = convert(date,getdate(),102)
FOR XML PATH ('') ) AS PERIODS,
r.GRADE
FROM register r
where r.CURRENT_STATUS = 'A'
and r.BUILDING ='2'
I'm getting all the students at building 2 and even if they don't have an attedance_c of T; a NULL value for Periods is being retrieved:
Student ID | Building | Period | Grade
12345 2 1 , 5 11
43210 2 1 , 12
95687 2 NULL 09
78417 2 NULL 10
20357 2 NULL 11
I have tried and ab.attendancePeriod is Not NULL and I still get the same results.
Any thoughts?
The outer query doesn't listen to any filters in the subquery; it will return NULL for any rows that aren't matched by the join conditions. You need to filter differently. Here is one way (this also eliminates the errant trailing comma, and avoids comparing dates by converting them expensively to strings):
;WITH x AS
(
SELECT DISTINCT s = r.Student_ID, r.building,
p = ab.attendancePeriod, r.grade
FROM dbo.Register AS r
INNER JOIN dbo.att_bottom AS ab
ON r.Student_ID = ab.Student_ID
AND r.building = ab.building
WHERE ab.building = '2'
AND ab.attendance_c = 'T'
AND ab.SCHOOL_YEAR = 2014
AND ab.attendance_date >= CONVERT(DATE, GETDATE())
AND ab.attendance_date < DATEADD(DAY, 1, CONVERT(DATE, GETDATE()))
AND r.building = '2'
AND r.CURRENT_STATUS = 'A'
)
SELECT DISTINCT
[Student ID] = x.s,
x.building,
Period = STUFF((SELECT ',' + x2.p FROM x AS x2 WHERE x2.s = x.s
FOR XML PATH(''),
TYPE).value(N'./text()[]',N'nvarchar(max)'),1,1,''),
x.grade
FROM x;
Another way:
SELECT DISTINCT
r.Student_ID,
r.building,
Period = STUFF(b.p.value(N'./text()[1]', N'nvarchar(max)'),1,1,''),
r.grade
FROM dbo.Register AS r
CROSS APPLY
(
SELECT p = ',' + ab.attendancePeriod
FROM dbo.att_bottom AS ab
WHERE ab.building = '2'
AND ab.attendance_c = 'T'
AND ab.SCHOOL_YEAR = 2014
AND ab.attendance_date >= CONVERT(DATE, GETDATE())
AND ab.attendance_date < DATEADD(DAY, 1, CONVERT(DATE, GETDATE()))
AND ab.student_id = r.student_id
AND ab.building = r.building
FOR XML PATH(''),TYPE
) AS b(p)
WHERE b.p IS NOT NULL
AND r.building = '2'
AND r.CURRENT_STATUS = 'A';
Move the AS PERIODS select to be an inner join to r.