SQL-Case When Issue - mysql

I am trying to sort the transaction dates into an aging policy. When LastDate has been in the location for greater than Aging Days limit policy it should show up as OverAge if not Within referring to the current date.
Here is the current table:
+---------+------+----------+-------------+
|LastDate | Part | Location | Aging Days |
+---------+------+----------+-------------+
12/1/2016 123 VVV 90
8/10/2017 444 RRR 10
8/01/2017 144 PR 21
7/15/2017 12 RRR 10
Here is the query:
select
q.lastdate,
r.part, r.location,
a.agingpolicy as 'Aging Days'
from opsintranexcel r (nolock)
left InventoryAging a (nolock) on r.location=a.location
left join (select part,MAX(trandate) as lastdate from opsintran group by
part) q on r.part=q.part
Here is the extra column I want added in:
+---------+------+----------+------------+---------+
|LastDate | Part | Location | Aging Days | Age |
+---------+------+----------+------------+---------+
12/1/2016 123 VVV 90 Overage
8/10/2017 444 RRR 10 Within
8/01/2017 144 PR 21 Within
7/15/2017 12 RRR 10 Overage
I appreciate your help.

I think below code will be work for you
SELECT
q.lastdate,
r.part,
r.location,
a.agingpolicy as 'Aging Days'
'Age' =
CASE
WHEN DATEDIFF( day, q.LastDate, GETDATE() ) > a.agingpolicy THEN 'Overage'
ELSE THEN 'Within'
END
FROM opsintranexcel r (nolock)
LEFT JOIN InventoryAging a (nolock) on r.location=a.location
LEFT JOIN (
SELECT part,MAX(trandate) as lastdate
FROM opsintran
WHERE trantype='II' and PerPost>='201601'
GROUP BY part) q ON r.part=q.part

you can check the difference of the current date and the lastdate value if over or within the aging days
CASE WHEN DATEDIFF(NOW(), q.lastdate) > a.agingpolicy
THEN 'Overage'
ELSE 'Within'
END AS age

You should modify your query as:
select
q.lastdate,
r.part, r.location,
a.agingpolicy as 'Aging Days',
if(DATEDIFF(NOW(), q.lastdate)) > a.agingpolicy, 'Overage','Within') as 'Age'
from opsintranexcel r (nolock)
left InventoryAging a (nolock) on r.location=a.location
left join (select part,MAX(trandate) as lastdate from opsintran where
trantype='II' and PerPost>='201601' group by part) q on r.part=q.part

Related

Query that reports the customers with the total purchases strictly increasing

In an interview I have been asked to return the SQL query that reports the IDs of customers with the total purchases strictly increasing yearly
Table is something like below:
And the output expected is 11,22 as 33 is not strictly increasing.
I did not able to solve it however, on googling I found the solution which is:
WITH year_cte AS (
SELECT customer_id,
YEAR(order_date) AS year,
SUM(price) AS total
FROM Orders
GROUP BY customer_id, year
ORDER BY NULL
)
SELECT a.customer_id
FROM year_cte a
LEFT JOIN year_cte b
ON b.customer_id = a.customer_id AND b.year = a.year + 1
GROUP BY a.customer_id
HAVING SUM(a.total >= IFNULL(b.total, 0)) = 1 // Did not get this SUM
ORDER BY NULL;
Now my problem is that I am able to understand the solution apart from 1 line which is:
HAVING SUM(a.total >= IFNULL(b.total, 0)) = 1 // Did not get this SUM
Can someone please help me to understand why there is a condition inside the sum() and why it is equating with 1?
The a table contains the total price for a customer_id for the year prior to table b.
You want to retrieve all customer_ids having total strictly increasing so you need to check that the number of rows satisfying the condition a.total >= IFNULL(b.total, 0) are 1.
This case happens when the customer has a strictly increasing streak of totals during the years, the = 1 accounts for the fact that the last line will have a value of b.total equal to NULL (and so a.total >= 0 will satisfy the condition).
Using customer_id = 11 as an example:
customer_id | year | total
--------------------------
11 | 2019 | 150
11 | 2020 | 200
11 | 2021 | 250
For a.year = 2019 and b.year = 2020 you will have 150 >= 200.
For a.year = 2020 and b.year = 2021 you will have 200 >= 250.
For a.year = 2021 and b.year = 2022 you will have 250 >= 0.
The SUM of this expression will give 1.

Selecting from 2 tables with possibly corresponding dates

I am looking for the correct query for my mysql db that has 2 seperate tables for lengths and weights.
I want to have the result returned as 1 query with 3 columns: datetime, length and weight.
The query should also allow to specify the user.
Eg.:
Table heights:
id user_id created_on height
1 2 2019-01-01 00:00:01 180
2 2 2019-01-02 00:00:01 181
3 3 2019-01-03 00:00:01 182
4 3 2019-01-04 00:00:01 183
5 2 2019-01-07 00:00:01 184
Table weights:
id user_id created_on weight
1 2 2019-01-01 00:00:01 80
2 2 2019-01-04 00:00:01 81
3 3 2019-01-05 00:00:01 82
4 3 2019-01-06 00:00:01 83
5 2 2019-01-07 00:00:01 84
I am looking to get the following result with a single query:
user_id created_on weight height
2 2019-01-01 00:00:01 80 180
2 2019-01-02 00:00:01 null 181
2 2019-01-04 00:00:01 81 null
2 2019-01-07 00:00:01 84 184
I have tried working with JOIN statements but fail to get the required result.
This join statement
SELECT w.* , h.* FROM weight w
JOIN height h
ON w.created_on=h.created_on
AND w.user_id=h.user_id AND user_id=2
will return only those results that have both a height and weight item for user_id and created_on
A full outer join would do the trick, however this is not supported by mysql.
The following query seems to be returning the required result, however it is very slow:
SELECT r.* FROM
(SELECT w.user_id as w_user, w.created_on as weightdate, w.value as weight, h.created_on as heightdate ,h.user_id as h_user, h.value as height FROM weight w
LEFT JOIN height h ON w.user_id = h.user_id
AND w.created_on=h.created_on
UNION
SELECT w.user_id as w_user, w.created_on as weightdate, w.value as weight, h.created_on as heightdate ,h.user_id as h_user, h.value as height FROM weight w
RIGHT JOIN height h ON w.user_id = h.user_id
AND w.created_on=h.created_on ) r
WHERE h_user=2 OR w_user =2
The query takes more than 3 seconds if the 2 tables have around 3000 entries.
Is there a way to speed this up, possibly using a different approach?
For extra bonus points: is it possible to allow for a small time discrepancy between both created_on datetimes? (eg. 10 minutes or within the same hour). Eg. if column weight has an entry for 2019-01-01 00:00:00 and table height has an entry for height at 2019-01-01 00:04:00 they appear in the same row.
Instead of using a calendar table to select dates of interest, you can use a UNION to select all the distinct dates from the heights and weights tables. To deal with matching times within an hour of each other, you can compare the times using TIMESTAMPDIFF and truncate the created_on time to the hour. Since this might create duplicate entries, we add the DISTINCT qualifier to the query:
SELECT DISTINCT COALESCE(h.user_id, w.user_id) AS user_id,
DATE_FORMAT(COALESCE(h.created_on, w.created_on), '%y-%m-%d %H:00:00') AS created_on,
w.weight,
h.height
FROM (SELECT created_on FROM heights
UNION
SELECT created_on FROM weights) d
LEFT JOIN heights h ON ABS(TIMESTAMPDIFF(HOUR, h.created_on, d.created_on)) = 0 AND h.user_id = 2
LEFT JOIN weights w ON ABS(TIMESTAMPDIFF(HOUR, w.created_on, d.created_on)) = 0 AND w.user_id = 2
WHERE h.user_id IS NOT NULL OR w.user_id IS NOT NULL
ORDER BY created_on
Output (from my demo, where I've modified your times to allow for matching within the hour):
user_id created_on weight height
2 19-01-01 01:00:00 80 180
2 19-01-02 00:00:00 181
2 19-01-04 04:00:00 81
2 19-01-07 06:00:00 84 184
Demo on dbfiddle
This is probably best handled using a calendar table, containing all dates of interest for the query. We can start the query with the calendar table, then left join to the heights and weights tables:
SELECT
COALESCE(h.user_id, w.user_id) AS user_id,
d.dt AS created_on,
w.weight,
h.height
FROM
(
SELECT '2019-01-01 00:00:01' AS dt UNION ALL
SELECT '2019-01-02 00:00:01' UNION ALL
SELECT '2019-01-03 00:00:01' UNION ALL
SELECT '2019-01-04 00:00:01' UNION ALL
SELECT '2019-01-05 00:00:01' UNION ALL
SELECT '2019-01-06 00:00:01' UNION ALL
SELECT '2019-01-07 00:00:01'
) d
LEFT JOIN heights h
ON d.dt = h.created_on AND h.user_id = 2
LEFT JOIN weights w
ON d.dt = w.created_on AND w.user_id = 2
WHERE
h.user_id IS NOT NULL OR w.user_id IS NOT NULL
ORDER BY
d.dt;
Demo

Mysql Query to Merge Two Condition Into One Row

I just get confused. Already tried to search this whole site or google but didn't find the 'nearest' solution.
Ok let's say I have this table structure.
id date finger_id finger_time is_enter
1 2017-03-30 2 09:00 1
2 2017-03-30 2 17:13 0
3 2017-03-31 4 09:10 1
4 2017-03-31 3 09:01 1
5. 2017-03-31 3 17:00 0
I want to make the table to be like below.
date finger_id enter_time exit_time
2017-03-30 2 09:00 17:13
2017-03-30 4 09:10
2017-03-31 3 09:10 17:00
I have made sql statement but it turns like this.
date finger_id enter_time exit_time
2017-03-30 2 09:00
2017-03-30 2 17:13
2017-03-31 4 09:10
2017-03-31 3 09:01
2017-03-31 3 17:00
I just want to know how to merge the is_enter 1 with is_enter 0 on the same date by the finger_id column.
Here's my sql query for the reference.
SELECT *
FROM `tbl_fingerprint`
LEFT JOIN `tbl_employee` ON `tbl_employee`.`fingerprint_id`=`tbl_fingerprint`.`fingerprint_id`
LEFT JOIN `tbl_position` ON `tbl_position`.`position_id`=`tbl_employee`.`position_id`
WHERE `fingerprint_date` >= '2017-03-01'
AND `fingerprint_date` <= '2017-04-01'
GROUP BY `tbl_fingerprint`.`fingerprint_id`,
`tbl_fingerprint`.`fingerprint_date`,
`tbl_fingerprint`.`is_enter`
ORDER BY `fingerprint_date` ASC LIMIT 30
Thanks for your help guys.
You can do a group by date and finger_id fields and use conditional expression (case or if()) within an aggregate function to get the expected outcome. The conditional statements within the aggregate function make sure that they return value only if the right value is set in is_enter field. I leave out the employee details, since those do not form part of your question:
SELECT date, fingerprint_id, max(if(is_enter=1,finger_time,null) as enter_time, max(if(is_enter=0,finger_time,null) as exit_time
FROM `tbl_fingerprint`
WHERE `fingerprint_date` >= '2017-03-01'
AND `fingerprint_date` <= '2017-04-01'
GROUP BY `tbl_fingerprint`.`fingerprint_id`,
`tbl_fingerprint`.`fingerprint_date`,
ORDER BY `fingerprint_date` ASC LIMIT 30
SELECT * FROM `tbl_fingerprint`
LEFT JOIN `tbl_employee` ON `tbl_employee`.`fingerprint_id`=`tbl_fingerprint`.`fingerprint_id`
LEFT JOIN `tbl_position` ON `tbl_position`.`position_id`=`tbl_employee`.`position_id`
LEFT JOIN (SELECT * FROM tbl_fingerprint WHERE is_enter = 0) a
ON a.finger_id = tbl_fingerprint.finger_id AND a.date = tbl_fingerprint.date
WHERE `fingerprint_date` >= '2017-03-01' AND `fingerprint_date` <= '2017-04-01' AND tbl_fingerprint.is_enter = 1
GROUP BY `tbl_fingerprint`.`fingerprint_id`, `tbl_fingerprint`.`fingerprint_date`, `tbl_fingerprint`.`is_enter`
ORDER BY `fingerprint_date` ASC LIMIT 30
Try This (This will work if finger_time is of time type):-
SELECT date, finger_id, min(finger_time) enter_time, if (min(finger_time) = max(finger_time), null, max(finger_time)) exit_time FROM xyz group by finger_id, date
SELECT a1.*, a3.time as time_out FROM attendance as a1
INNER JOIN (SELECT MIN(id) as id FROM attendance where is_enter = '1' group by date, f_id ) as a2
ON a2.id = a1.id
LEFT JOIN attendance as a3 ON a3.date = a1.date AND a1.f_id = a3.f_id and a3.is_enter = '0'
you may need to cast the date to not include the time portion or to char with the yyyy-mm-dd format

Return additional column value for MySQL Union Query

I have two queries that get the:
Most recent snow measurement (within 1 hour)
And a measurement taken 24 hours ago.
When I UNION the queries, I am expecting the two measurements to be in separate columns, but they are actually returned in separate (duplicate) rows. See output below.
SELECT snowfall.cms as now_snow, resorts.*, regions.name
FROM snowfall
INNER JOIN resorts on resorts.id = snowfall.resort_id
INNER JOIN regions ON resorts.region_id = regions.id
WHERE snowfall.timestamp >= SUBDATE( NOW( ) , INTERVAL 1 HOUR )
GROUP BY resorts.id
UNION
SELECT snowfall.cms as 24hr_snow, resorts.*, regions.name
FROM snowfall
INNER JOIN resorts on resorts.id = snowfall.resort_id
INNER JOIN regions ON resorts.region_id = regions.id
WHERE snowfall.timestamp >= SUBDATE( NOW() , INTERVAL 1 DAY)
GROUP BY resorts.id
ORDER BY now_snow DESC
I am getting a result of:
now_snow | resorts.name | ...
========================================
20 | The Mountain
15 | The Mountain
18 | The Hill
102 | The Hill
But was expecting a result of:
now_snow | 24hr_snow | resorts.name | ...
========================================
20 | 15 | The Mountain
18 | 102 | The Hill
Is UNION correct in this scenario? How can I achieve the desired output?
A UNION will append rows to your query. In order to broaden your query you need a join.
I would suggest a self join in this case. One snowfall for daily, one for hourly. Both joined on resort id and grouped by resort name.
SELECT sum(hourly.cms) as now_snow, sum(daily.cms) as 24hr_snow, resorts.name
FROM
snowfall daily
INNER JOIN resorts on resorts.id = daily.resort_id
INNER JOIN regions ON resorts.region_id = regions.id
INNER JOIN snowfall hourly on resorts.id = hourly.resort_id
WHERE daily.timestamp >= SUBDATE( NOW() , INTERVAL 1 DAY)
AND hourly.timestamp >= SUBDATE( NOW( ) , INTERVAL 1 HOUR )
GROUP BY resorts.name
ORDER BY now_snow DESC
You didnt use an aggregate in your post and mysql may allow for that, but I've added it here for clarity.

Missing records from one table in SQL Server 2008R2

Table 1:
Date PlacementID CampaignID Impressions
04/01/2014 100 10 1000
04/01/2014 101 10 1500
04/01/2014 100 11 500
Table 2:
Date PlacementID CampaignID Cost
04/01/2014 100 10 5000
04/01/2014 101 10 6000
04/01/2014 100 11 7000
04/01/2014 103 10 8000
When I have joined this table using Full Join and Left Join statement, I am not able to get uncommon record which is last row in table2 that display PlacementID 103 and campaignID 10 and Cost 8000. However I have searched all raw data and file but this missing records are not common between two sources. However, I want to include this records in final table. How can I do that? This two table are two different source and I have got results only common records.
Moreover, when I found out that missing value is exact value that are required in final figure so want to include every thing. I am including my SQL script below:
SELECT A.palcementid,
A.campaignid,
A.date,
Sum(A.impressions) AS Impressions,
Sum(CASE
WHEN C.placement_count > 1 THEN ( B.cost / C.placement_count )
ELSE B.cost
END) AS Cost
FROM table1 A
FULL JOIN table2 B
ON A.placementid = B.placementid
AND A.campaignid = B.campaignid
AND A.date = B.date
LEFT JOIN (SELECT Count(A.placementid) AS Placement_Count,
placementid. campaignid,
date
FROM table1
GROUP BY placementid,
campaignid,
date) c
ON A.placementid = C.placementid
AND A.campaignid = C.campaignid
AND A.date = C.date
GROUP BY A.placementid,
A.campaignid,
A.date
I am dividing Cost by placement because in source the cost was allocated for one placement only and one time so I have to divide those because in actual table the same Placementid repeat more than 1 times on same date.
As you didn't provide any expected output I guessing here but if the result you want is this:
PlacementID CampaignID Date Impressions Cost
----------- ----------- ----------------------- ----------- -----------
100 10 2014-04-01 02:00:00.000 1000 5000
100 11 2014-04-01 02:00:00.000 500 7000
101 10 2014-04-01 02:00:00.000 1500 6000
103 10 2014-04-01 02:00:00.000 NULL 8000
Then the following query should do it:
SELECT COALESCE(A.PlacementID,b.placementid) AS PlacementID,
COALESCE(A.campaignid, b.campaignid) AS CampaignID,
COALESCE(A.date, b.date) AS [Date],
SUM(A.impressions) AS Impressions,
SUM(CASE
WHEN C.placement_count > 1 THEN ( B.cost / C.placement_count )
ELSE B.cost
END ) AS Cost
FROM table1 A
FULL JOIN table2 B
ON A.[PlacementID] = B.placementid
AND A.campaignid = B.campaignid
AND A.date = B.date
LEFT JOIN (SELECT COUNT(PlacementID) AS Placement_Count,
placementid, campaignid,
date
FROM table1
GROUP BY placementid,
campaignid,
date) c
ON A.[PlacementID] = C.placementid
AND A.campaignid = C.campaignid
AND A.date = C.date
GROUP BY COALESCE(A.PlacementID, B.PlacementID),
COALESCE(A.campaignid, b.campaignid),
COALESCE(A.date, b.date)
Sample SQL Fiddle