Need to find whether overlap is present in sql - sql-server-2008

I haev been stuck in a problem from quite some time and trying to figure out how to solve this using sql.
I have a table which has 3 columns :
LowerLimit UpperLimit Code
1 10 A
10.01 20 B
20.01 40 C
40.01 100 D
So in such case I need to check if there overlap present or not. The Upperlimit should not match with the LowerLimit of the next row and the permissible difference is only 0.01 . Is it possible to solve this using queries or do I need to iterate the whole range and find whether there is no overlap???
Any help is appreciated.

You can do this with exists to get the first row of overlap. For your specific logic:
select t.*
from t
where exists (select 1
from t t2
where t2.upperlimit >= t.lowerlimit and
t2.upperlimit < t.upperlimit + 0.01
);
If you want both rows, you can formulate this as a join or using a second exists to get the previous row.
I don't like your data representation. I would simple make the lower bound inclusive and the upper bound exclusive. Then the next lower bound could simply be the previous upper bound. You would not be able to use between but that is a bad idea anyway on numbers with decimal parts.

Related

MYSQL Alternative to UNION for same table reusing same columns selected as new name

I'm trying to generate a result set from a table with effectively a unique/primary key as billyear, billmonth and type along with cost and consumption. So there could be 3 bill year and bill month identical entries but the type could be one of three values: E, W or NG.
I need to create a result set that has just one row per billyear and billmonth entry.
(
select month as billmonth, year as billyear, cost_estimate as eleccost, consumption_estimate as eleccons from tblbillforecast where buildingid=19 and type='E'
)
UNION (
select month as billmonth, year as billyear, cost_estimate as gascost, consumption_estimate as gascons from tblbillforecast where buildingid=19 and type='NG'
)
UNION (
select month as billmonth, year as billyear, cost_estimate as watercost, consumption_estimate as watercons from tblbillforecast where buildingid=19 and type='W'
)
This generates a result set with only billmonth, billyear, eleccost and eleccons columns. I've tried all kinds of solutions but the above example is the simplest to show where it's going wrong.
Additionally it still has 3 rows per billmonth/billyear unique combination instead of merging to one.
UPDATE:
Sample data
SELECT month AS billmonth,
year AS billyear,
SUM(CASE type WHEN 'E' THEN cost_estimate END) AS eleccost,
SUM(CASE type WHEN 'NG' THEN cost_estimate END) AS gascost,
SUM(CASE type WHEN 'W' THEN cost_estimate END) AS watercost
FROM tblbillforecast
WHERE buildingid=19
GROUP BY billmonth, billyear;
Result:
Expected result, eg:
year | month | eleccost | gascost | watercost
2018 | 1 | 32800 | 4460 | 4750
This is behaving correctly. An SQL query result set has one name per column, and this name applies to all the rows. So if you try to rename the column in the second or subsequent queries of the UNION, those new names are ignored. The name of the column is determined only by the first query of the UNION.
Additionally it still has 3 rows per billmonth/billyear unique combination instead of merging to one.
That's also correct behavior, according to the query you tried. UNION does not merge multiple rows into one, it only appends sets of rows.
As Akina hinted in the comments above, you may use multiple columns:
SELECT month AS billmonth,
year AS billyear,
SUM(CASE type WHEN 'E' THEN cost_estimate END) AS eleccost,
SUM(CASE type WHEN 'NG' THEN cost_estimate END) AS gascost,
SUM(CASE type WHEN 'W' THEN cost_estimate END) AS watercost
FROM tblbillforecast
WHERE buildingid=19
GROUP BY billmonth, billyear;
This uses GROUP BY to "merge" rows together, so you get one row in the result per month/year.
A quick bit of guidance on various data shaping operations in SQL:
JOIN - makes resultsets wider (more columns) by bringing together tables/resultsets in a side-by-side fashion generating output rows that have all the columns of the two input column sets
SELECT - typically makes resultsets narrower by allowing you to specify which columns you're interested in and which you are not; by not mentioning an available column it disappears meaning you output fewer columns
UNION - makes resultsets taller (more rows) by bringing together resultsets and outputting one on top of the other. Because columns always have a fixed data type and one name, you must have the same number of and type of, and order of columns
WHERE - makes resultsets shorter (fewer rows) by allowing you to specify truth based filters that exclude rows
It's not hard and fast; you can use select to create more columns too, but just in a very rudimentary sense these concepts hold true - JOIN to widen, UNION for taller, SELECT for narrower and WHERE for shorter. All the work you do with SQL is a data shaping exercise; you're either paring a rectangular block of data down or extending it, and in either a vertical or horizontal direction (or a mix).
I'm not going to get into grouping because that mixes rows up, and isn't something you tried in the question.. The reason for me writing this out was purely because you'd attempted to use a UNION (height-increasing) operation when you actually wanted a widen which, regardless of how it is done (JOIN or as per Bill's answer a SELECT+GROUP, which is valid, but relies on the "mixes rows up" aspect of grouping), specifically isn't done with a UNION. Union only makes stuff taller.
To give an example of how it might be done in an alternative way to Bill's approach, this task of yours has one huge table that is "too tall" - it uses 3 rows where 1 would do, if only it were a bit wider. That is to say if only there were 3 columns for electric/gas/water then we wouldn't need 3 rows with 1 utility in each.
Of course, we have this "one utility per row" because it is very flexible. Database tables don't have varying numbers of columns but they DO have varying numbers of rows. If a new bill type came along tomorrow - internet - no table changes are needed to accommodate it; add a new type I, and away you go, adding another row. We now store 4 rows of 1 utility where 1 row with 4 columns would do, but crucially we didn't have to change the table structure. We could have infinite different kinds of bills, and not need infinite columns because we can already have infinite rows
So you want to reshape your data from 4-rows-by-1-column to 1-row-by-4-columns. It could be solved as :
narrow the table to just year,month,building,type,cost AND shorten it to just electricity
separately narrow the table to just year,month,building,type,cost AND shorten it to just gas
separately narrow the table to just year,month,building,type,cost AND shorten it to just water
join (widening) all these newly created result sets , then narrow to remove the repeated year,month,building,type columns
That would look like:
SELECT e.year, e.month, e.building, e.cost, g.cost, w.cost
FROM
(SELECT year,month,building,cost FROM t WHERE type = 'E') e
JOIN
(SELECT year,month,building,cost FROM t WHERE type = 'NG') g
ON
e.year = g.year AND e.month = g.month AND e.building = g.building
JOIN
(SELECT year,month,building,cost FROM t WHERE type = 'W') w
ON
e.year = w.year AND e.month = w.month AND e.building = w.building
WHERE
e.building = 19
You can see clearly the 3 narrowing-and-shortening operations that pick out "just the gas", "just the electric", and "just the water" - they're the (SELECT year,month,building,cost FROM t WHERE type = 'NG') and that's what reduces the height of the original table, making it three times shorter than it was in each case. If we had 999 rows X 5 cols in the big table it goes to 3 sets of 333 x 5 rows each
You can see that we then JOIN these together to widen the results - our e.g 3 sets of 333 x 5 rows each widens to 333 x 15 when JOINed..
Then went from 333x15 down to 333 X 7 when SELECTed to ditch the repeated columns
It's likely not perfect (I'd perhaps left join all 3 onto a 4th set of numbers that are just the common columns in case some utilities aren't present for a particular month), and perhaps some people will come along complaining that it's less performant because it hits the table 3 times.. All that is accessory to the point I'm making about SQL being an exercise in reshaping data - tables are the starting blocks of data and you cut them up narrower and shorter, then stick them together side by side, or on top of each other and that becomes your new data block that's maybe wider, higher, both.. In any case it's definitely a different shape to what you started with. And then you can cut and shape again, and again..
Go with Bill's conditional agg (though this way would be fine if there is one row per building/year/month) but take away a stronger notion about in what direction these common operations (SELECT/JOIN/WHERE/UNION) reshape your data
Footnote about Bill's conditional aggregation (I know I said I wouldn't talk about it but it might make more sense to now). If you have:
Type, Cost
E, 123
NG, 456
W, 789
And you do a
SELECT
CASE WHEN Type = 'E' THEN Cost END as CostE,
CASE WHEN Type = 'NG' THEN Cost END as CostG,
CASE WHEN Type = 'W' THEN Cost END as CostW
...
It spreads the data out over more columns - the data has "gone from vertical to diagonal"
CostE, CostNG, CostW
123, NULL, NULL
NULL, 456, NULL
NULL, NULL, 789
But it's still too tall. If you then run a GROUP BY, which mixes rows up and ask for e.g. just the MAX from each column, then all the NULLs will disappear (because there is a non null somewhere in the column, and NULL is lost if there is a non null, no matter what you're doing) and the rows collapse, mixing together, into one:
CostE, CostNG, CostW
123, 456, 789
The data has pivoted round from being vertical, to being horizontal - another data shaping. It was pulled wider, and squashed flatter

MySQL query to check if a part of a dates interval is contained within another dates interval

Given the MySQL table:
occupied_room_dates
id (int) date_a (DATE) date_b (DATE)
1 2018-12-20 2018-12-25
5 2019-01-05 2019-02-15
36 2019-01-02 2019-03-21
And given two variables containig date strings in the format yyyy/mm/dd:
DT_A = yyyy/mm/dd (Example: 2019-02-03)
DT_B = yyyy/mm/dd (Example: 2019-05-03)
I want to perform a query to check if for given DT_A and DT_B (being DT_A < DT_B), there is at least one row in the table occupied_room_dates for which the dates intervals:
a) [date_a - date_b]
b) [DT_A - DT_B]
overlap each other.
Examples / Expectation:
With the example, for ROOM_ID = 36, it should return the third row in the example table, since the dates intervals overlap.
For another example (ROOM_ID = 36, DT_A = 2019-06-15, DT_B = 2019-07-15) there should be no match, since there is no dates interval overlaping it.
What I tried so far is:
Since there are four generic ways of the overlap happening, I am creating 4 conditions that will make the alarm fire:
"SELECT `date_a`,`date_b` FROM `occupied_room_dates`
WHERE `id`='".ROOM_ID."' AND
(
(`date_a`<='".DT_A."' AND `date_b`>='".DT_A."')
OR (`date_a`<='".DT_B."' AND `date_b`>='".DT_B."')
OR (`date_a`>='".DT_A."' AND `date_b`<='".DT_B."')
OR (`date_a`<='".DT_A."' AND `date_b`>='".DT_B."')
) LIMIT 1"
But trying this on a real table containing a decent amount of rows, it is not working as expected. And I am having trouble with visualizing all the conditions that must be met, as well as with representing those conditions in a single MySQL query.
The purpose behind this is to check if a room is occupied in a date interval. Could you please help me with the query?
Explanation:
Your code pretty much seems right. But since you only have data from one table, you don't need Where ID = "Room_ID" as such. I didn't understand what exactly you wanted to output out of the 3 columns so I have done Select *, if you only want the RoomID, use SELECT id FROM occupied_room_dates instead.
Code:
SELECT * FROM occupied_room_dates
WHERE (date_a<="2019-02-03" AND date_b>="2019-02-03")
OR (date_a<="2019-05-03" AND date_b>="2019-05-03")
OR (date_a>="2019-02-03" AND date_b<="2019-05-03")
OR (date_a<="2019-02-03" AND date_b>="2019-05-03")

How to select two MySQL rows and then compare a column and return an output

I've a table with a structure something like this,
Device | paid | time
abc 1 2 days ago
abc 0 1 day ago
abc 0 5 mins ago
Is it possible to write a query that checks the paid column on all the rows where Device = abc and then outputs the most recent two rows that different. Basically, something like an if statement saying if row 1 = 1 and row 2 = 0 output that but only if it's the most recent two columns that are different. For example, in this case, the first and second row. The table is being updated whenever a user changes from a free to paid account etc. It is also updated in different columns for different reasons hence the duplicate 0s for example.
I know this would probably be done better by having another table altogether and updating that every time the user switches account type, but is there any way to make this work?
Thanks
Example:
http://rextester.com/MABU7860 need further testing on edge cases but this seems to work.
SELECT A.*, B.*
FROM SQLfoo A
INNER JOIN SQLFoo B
on A.Device = B.Device
and A.mTime < B.mTime
WHERE A.Paid <> B.Paid
and A.device = 'abc'
ORDER BY B.mTime Desc, A.MTime Desc
LIMIT 1
By performing a self join we on the devices where the time from one table is less than the time from the next table (thus the two records will never matach and we only get the reuslts one way) and we order by those times descending, the highest times appear first in the result since we limit by a single device we don't need to concern ourselves with the devices. We then just need compare the paid from one source to the paid in the 2nd source and return the first result encountered thus limit 1.
Or using user variables
http://rextester.com/TWVEVX7830
in other engines one might accomplish this task by performing the join as in above, assigning a row number partitioned by the device and then simply return all those row_numbers with a value of 1; which would be the earliest date discrepency.
Use LIMIT to limit the number of record on mysql:
http://www.mysqltutorial.org/mysql-limit.aspx
In your case, use LIMIT 2
and then put the 2 record that you just select into an array, then compare the array if the value is different. If they are different then print

Identifying record in mysql table based on difference between two specific rows

I would really appreciate some help with a MySQL query for the following matter.
My table, let's call it "data", contains the following fields: "timestamp" and "temperature".
Every 30 seconds a new record is being added into it.
My goal is to identify the record (timestamp) which compared to the one added 2 minutes later (4 records later) has a temperature difference of 20 degrees (or more)
Ex.
...
19:14:08 99
19:14:38 100
19:15:08 101
19:15:38 105
19:16:08 115
19:16:38 126
19:17:08 150
19:17:38 151
...
In this case, the timestamp which I have to find is 19:14:38, because if compared to the one at 19:16:38, we have 126-100 = 26 > 20.
There are some other conditions (not worth mentioning) which have to be met as well, but at least those I can handle myself.
Thanks for your help.
If your timestamps are exactly, you can use a self-join:
select t.*
from t join
t tnext
on t.timestamp = tnext.timestamp - interval 2 minutes
where tnext.temperature - t.temperature > 20;
This is highly dependent on the accuracy of your timestamps, however.
So I believe you're on the right track with a join to the same table: something like this should get you started. It's untested air-code and typically I write in oracle sql so pardon any syntax nuances...
SELECT
a.TEMPERATURE AS NEW_TEMP
,b.TEMPERATURE AS PRIOR_TEMP
FROM
DATA a
INNER JOIN DATA b ON
b.TIMESTAMP = a.TIMESTAMP-TO_DATE('02','MM')
AND ABS(ABS(a.TEMPERATURE) - ABS(b.TEMPERATURE)) > 20
Additionally - using a timestamp is probably not as reliable as you might think since there could be variation that you do not want to exist (such as the timestamp may be off by a second ie it is exactly 1:59 seconds prior to the new record. in which case this join would miss it. whereas if you were using an autoincremented ID as suggested above, you could simply replace that first join clause with:
b.RECORD_ID = a.RECORD_ID-4

mysql find rows with fractional part of number

Using the MySQL command prompt, I want to SELECT all rows conataining a specific fractional part of number.
I have a field of type FLOAT called priority & I want to find all rows where the priority is X.2 where X can be any whole number.
Is there a way to use the MOD or FLOOR function to extract the ".2" in a query?
I've tried:
SELECT * from table
WHERE priority-FLOOR(priority) = .2 *(note the decimal point before the 2)*
but it returns empty set when I know there are at least 100 rows containing a priority of X.2
Thanks
What about using the LIKE clause in MySQL?
SELECT * FROM table WHERE priority LIKE %.2%
Floating point numbers are notoriously fickle to work with. That is why SQL offers the decimal data type, which is fixed length.
Do what you want with between:
SELECT *
from table
WHERE priority-FLOOR(priority) between 0.15 and 0.25
Consider storing the priority as a decimal instead of a floating point number.
If you have "deeper" priorities, like 1.22 that you are trying to avoid, then do:
WHERE priority-FLOOR(priority) between 0.195 and 0.205
The range can be even narrower, if you need.