SQL Check Datetime Existence within multiple columns - sql

I've a BillDate as date, and a Mark as bit column in First table.
(Mark=0 by default)
In Second table I've FromDate as date, and ToDate as date Column.
I want to set Mark=1 if BillDate is exists between FromDate & ToDate
Let Say In First Table the data is
----------------------------
BillDate | Mark
----------------------------
2012-11-10 11:15:30 | 0
2012-12-12 09:00:00 | 0
In Second Table the data is
---------------------------------------------
FromDate | ToDate
---------------------------------------------
2012-11-01 07:00:00 | 2012-11-09 23:59:59
2012-12-08 07:00:00 | 2012-12-15 23:59:59
So in the above scenario only the second row from First table
which is having, BillDate->2012-12-12 09:00:00 will be Mark as 1
because it comes between second row of second table
I hope I've explained my scenario,

You haven't said what flavor of SQL you are using and if there are any other fields which link your tables. Therefore assuming a cross join how about this:
Update ft
SET mark = 1
FROM FirstTable ft, SecondTable st
WHERE BillDate BETWEEN StartDate AND EndDate

I think this will work in most/all DMBS (as none specified)
UPDATE FirstTable
SET Mark = 1
WHERE EXISTS
( SELECT 1
FROM SecondTable
WHERE FirstTable.BillDate BETWEEN SecondTable.Fromdate AND SecondTable.ToDate
);

Related

Select record by given date from and date till.

I having the records as below
StartDate | EndDate | ID
---------------------------------
25-12-2016 30-12-2016 0
01-01-2017 05-01-2017 1
10-01-2017 12-01-2017 2
01-02-2017 05-02-2017 3
By given selecting the Date Range from 02-01-2017 till 11-01-2017 , How do we select the record Startdate n EndDate that is fall on between the Date Range given as expected?
Would like to expect table result as below
StartDate | EndDate | ID
------------------------------
01-01-2017 05-01-2017 1
10-01-2017 12-01-2017 2
So, basically you are asking how to check if two date ranges overlap.
The way to do this is to check that one starts before the other ends, while the other starts before one ends. You can see a visualization in the overlap tag wiki.
Your query should be something like this:
SELECT StartDate, EndDate, ID
FROM YourTable
WHERE StartDate <= '11-01-2017'
AND EndDate >= '02-01-2017'
Try the below query,
DECLARE #V_START_DATE DATETIME = '2017-01-02'
,#V_END_DATE DATETIME = '2017-01-11'
SELECT *
FROM #TABLE
WHERE StartDate BETWEEN #V_START_DATE AND #V_END_DATE
OR EndDate BETWEEN #V_START_DATE AND #V_END_DATE
Try the below Query
SELECT * FROM DateRanges
WHERE StartDate BETWEEN '02-01-2017' and '11-01-2017'
OR ENDdate BETWEEN '02-01-2017' and '11-01-2017'

Need in and out to come in same line and remove null values

I am having a table called alfa
intime | outime | ID | Accountid |
-----------------------------------------------------------------------------------
2016-06-23 06:53:00.000 | NULL | 1 | 1234 |
NULL | 2016-06-23 17:04:00.000 | 2 | 1234 |
I am expecting the output like
intime | outime | Accountid |
-------------------------------------------------------------------------------------------------
2016-06-23 06:53:00.000 | 2016-06-23 17:04:00.000 | 1234 |
I am trying to run this query:
select * from alfa
where intime is not null
and exists
(select * from alfa as a
where a.accountid=alfa.accountid
and out is not null)
But it not giving me the output as expected.
Without knowing full data the query might be way wrong, but assuming you will always have only 2 records for the same AccountId (one record where intime is null and one record where outime is null), you can use join on the same table to get the data you need;
SELECT a.intime, b.outime, a.AccountId
FROM alfa a
LEFT JOIN alfa b ON a.AccountId = b.AccountId
AND CONVERT(date, a.intime) = CONVERT(date, b.outime)
AND b.intime is null
WHERE a.outime is null
Here I used alias a and filtered only records that have outime value null. Then I joined it to alias b (where records only have intime value of null) based on AccountId.
The query assumes that you will always have records where outime is null, but not necessarily where intime is null, therefore LEFT JOIN was used.
UPDATE: Based on comment I added another condition to JOIN - joining date only from intime to date only from outime. The method for extracting the date only is based on this answer.
Please see if this is what you need.
SELECT MIN(t.intime),MAX(isnull(t.outime,0)),accountid
FROM alfa t
group by t.accountid

sql how to add missing week to the table

I have a table that has this data: Date when the employees reported and the week start-date(Monday) for that week. Now they did not work all the dates. For example there is no data on week of christmas. Is there a way I can add the missing week.So, I will still have the week start-date for each and every week. But the report-date can be null.
I cannot declare variables
This is what I have
and this is what i want to add the missing week
Query
SQLFIDDLEEXAMPLE:
CREATE TABLE tb
(
d1 date,
d2 date
);
INSERT INTO tb
(d1, d2)
VALUES
('2015-12-10', '2015-12-07'),
('2015-12-15', '2015-12-14'),
('2015-12-29', '2015-12-28'),
('2016-01-05', '2016-01-04');
SET DATEFIRST 1
INSERT INTO tb
( d1, d2 )
select null, DATEADD(day,number,'2015-01-01')
FROM master..spt_values t1
LEFT JOIN tb t2
ON DATEADD(day,number,'2015-01-01') = t2.d2
WHERE type = 'P'
AND DATEADD(day,number,'2015-01-01') >= '2015-12-01'
AND DATEADD(day,number,'2015-01-01') <= '2016-01-04'
AND DATEPART(weekday,DATEADD(day,number,'2015-01-01')) = 1
AND t2.d2 is null
SELECT *
FROM tb
Result:
| d1 | d2 |
|------------|------------|
| 2015-12-10 | 2015-12-07 |
| 2015-12-15 | 2015-12-14 |
| 2015-12-29 | 2015-12-28 |
| 2016-01-05 | 2016-01-04 |
| (null) | 2015-12-21 |
You can create a new Calendar/Weeks table containing all the weeks in the year. This table should be in advance.
You can then make a reference from your data table to this calendar table (by id or week/year).
Your report should be based on the calendar table with an outer join to your data table.
This way your report will contain all weeks even if some weeks don't have any data.
EDIT: You would need a new table like this:
Week:
| Start date | End date |
| 12/07/15 | 12/13/15 |
| 12/14/15 | 12/20/15 |
| 12/21/15 | 12/27/15 |
etc...
Assuming that #weekly_calendar table contains your valid work weeks (i.e., for Dec 2015). By the way, syntax is for MSSQL. You should specify what database you are using.
You can also dynamically create the calendar on run-time. This is just to show the concept in an easy to understand way.
-- week start dates
-- 2015-12-01
-- 2015-12-07
-- 2015-12-14
-- 2015-12-21
-- 2015-12-28
create table #weekly_calendar (
week_start_date datetime,
week_end_date datetime
)
Assuming that #report_date contains the report date of the employee.
-- report dates
-- 2015-12-02
-- 2015-12-15
-- 2015-12-29
create table #report_date (
report_date datetime
)
This is how you display the unreported dates.
select * from #weekly_calendar w
left join #report_date r
on r.report_date between w.week_start_date and w.week_end_date
If you do not have the week_end_date. Again, assuming your work days start from Monday to Friday.
select * from #weekly_calendar w
left join #report_date r
on r.report_date between w.week_start_date and DATEADD(dd, 6-(DATEPART(dw, w.week_end_date)), w.week_end_date)

Query Max/Min value shows original values

I'm taking the max value and min value of a table composed of Date, Time, and Load. For example:
Date | Time | Temp
-------------------------------------
1/1/2014 | 09:00:00 AM | 100
-------------------------------------
1/1/2014 | 09:01:00 AM | 110
-------------------------------------
1/1/2014 | 09:02:00 AM | 120
-------------------------------------
1/1/2014 | 09:03:00 AM | 111
-------------------------------------
....................And so on
I've tried to just use the functions Min(), Max() but these values output the same data as the original table. See SQL code:
SELECT Table1.Date, Table1.Time, Min(Table1.Temp) AS MinLoad
FROM Table1
GROUP BY Table1.Date, Table1.Time;
I tried using DMin() and DMax() functions but instead of getting a value I got a null of the values. I tried the syntax
DMin("[Temp]", "[Table1]", [Time] Between #09:00# And #15:00#)
I'm fairly new to Access so any help would be appreciated.
Thanks!
Figured it out:
SELECT Date.DateLog, Min(Table1.Data) AS MinOfData
FROM [Date] INNER JOIN Table1 ON Date.DateLog = Table1.Date
GROUP BY Date.DateLog;

Finding correlated values from second table without resorting to PL/SQL

I have the following two tables in my database:
a) A table containing values acquired at a certain date (you may think of these as, say, temperature readings):
sensor_id | acquired | value
----------+---------------------+--------
1 | 2009-04-01 10:00:00 | 20
1 | 2009-04-01 10:01:00 | 21
1 | 2009-04 01 10:02:00 | 20
1 | 2009-04 01 10:09:00 | 20
1 | 2009-04 01 10:11:00 | 25
1 | 2009-04 01 10:15:00 | 30
...
The interval between the readings may differ, but the combination of (sensor_id, acquired) is unique.
b) A second table containing time periods and a description (you may think of these as, say, periods when someone turned on the radiator):
sensor_id | start_date | end_date | description
----------+---------------------+---------------------+------------------
1 | 2009-04-01 10:00:00 | 2009-04-01 10:02:00 | some description
1 | 2009-04-01 10:10:00 | 2009-04-01 10:14:00 | something else
Again, the length of the period may differ, but there will never be overlapping time periods for any given sensor.
I want to get a result that looks like this for any sensor and any date range:
sensor id | start date | v1 | end date | v2 | description
----------+---------------------+----+---------------------+----+------------------
1 | 2009-04-01 10:00:00 | 20 | 2009-04-01 10:02:00 | 20 | some description
1 | 2009-04-01 10:10:00 | 25 | 2009-04-01 10:14:00 | 30 | some description
Or in text from: given a sensor_id and a date range of range_start and range_end,
find me all time periods which have overlap with the date range (that is, start_date < range_end and end_date > range_start) and for each of these rows, find the corresponding values from the value table for the time period's start_date and end_date (find the first row with acquired > start_date and acquired > end_date).
If it wasn't for the start_value and end_value columns, this would be a textbook trivial example of how to join two tables.
Can I somehow get the output I need in one SQL statement without resorting to writing a PL/SQL function to find these values?
Unless I have overlooked something blatantly obvious, this can't be done with simple subselects.
Database is Oracle 11g, so any Oracle-specific features are acceptable.
Edit: yes, looping is possible, but I want to know if this can be done with a single SQL select.
You can give this a try. Note the caveats at the end though.
SELECT
RNG.sensor_id,
RNG.start_date,
RDG1.value AS v1,
RNG.end_date,
RDG2.value AS v2,
RNG.description
FROM
Ranges RNG
INNER JOIN Readings RDG1 ON
RDG1.sensor_id = RNG.sensor_id AND
RDG1.acquired => RNG.start_date
LEFT OUTER JOIN Readings RDG1_NE ON
RDG1_NE.sensor_id = RDG1.sensor_id AND
RDG1_NE.acquired >= RNG.start_date AND
RDG1_NE.acquired < RDG1.acquired
INNER JOIN Readings RDG2 ON
RDG2.sensor_id = RNG.sensor_id AND
RDG2.acquired => RNG.end_date
LEFT OUTER JOIN Readings RDG1_NE ON
RDG2_NE.sensor_id = RDG2.sensor_id AND
RDG2_NE.acquired >= RNG.end_date AND
RDG2_NE.acquired < RDG2.acquired
WHERE
RDG1_NE.sensor_id IS NULL AND
RDG2_NE.sensor_id IS NULL
This uses the first reading after the start date of the range and the first reading after the end date (personally, I'd think using the last date before the start and end would make more sense or the closest value, but I don't know your application). If there is no such reading then you won't get anything at all. You can change the INNER JOINs to OUTER and put additional logic in to handle those situations based on your own business rules.
It seems pretty straight forward.
Find the sensor values for each range. Find a row - I will call acquired of this row just X - where X > start_date and not exists any other row with acquired > start_date and acquired < X. Do the same for end date.
Select only the ranges that meet the query - start_date before and end_date after the dates supplied by the query.
In SQL this would be something like that.
SELECT R1.*, SV1.aquired, SV2.aquired
FROM ranges R1
INNER JOIN sensor_values SV1 ON SV1.sensor_id = R1.sensor_id
INNER JOIN sensor_values SV2 ON SV2.sensor_id = R1.sensor_id
WHERE SV1.aquired > R1.start_date
AND NOT EXISTS (
SELECT *
FROM sensor_values SV3
WHERE SV3.aquired > R1.start_date
AND SV3.aquired < SV1.aquired)
AND SV2.aquired > R1.end_date
AND NOT EXISTS (
SELECT *
FROM sensor_values SV4
WHERE SV4.aquired > R1.end_date
AND SV4.aquired < SV2.aquired)
AND R1.start_date < #range_start
AND R1.end_date > #range_end