Oracle SQL WHERE within a time range using sysdate - sql

I'm trying to select data from the previous day, and within a certain time frame, but I may be calculating my where clause incorrectly. I've tried switching times around etc. Basically I want to see all data from 6am-6pm, and then 7pm-3am, but My results aren't relecting such. I've tried between trunc(sysdate)-1 '00:00:00'<- but specifying the time, but I feel I'm not familiar enough with the function.
Note: DB is in UTC hence the 8/24.
Query:
--TOTAL PROBLEM STOW EVENTS
SELECT to_char(entry_date -8/24, 'DD-MON-YYYY HH12:MI:SSam'), OLD_BIN_ID old_bin, NEW_BIN_ID NEW_BIN, ISBN ASIN, QUANTITY
FROM BINEDIT_ENTRIES
WHERE ENTRY_DATE BETWEEN trunc(SYSDATE) -1 +4/24 AND trunc(SYSDATE) -1 +16/24
--where entry_date BETWEEN trunc(sysdate)-1 '00:00:00' AND trunc(sysdate)-1 '00:00:00.000'
AND substr(old_bin_id,1,2) = 'SC'
AND substr(new_bin_id,1,2) = 'vt'
GROUP BY ENTRY_DATE, OLD_BIN_ID, NEW_BIN_ID, ISBN, Quantity
ORDER BY QUANTITY DESC;
Result:
This appears to look correct, BUT when I change to look at other time range, it shows me this..
Second Query(Night Time):
--TOTAL PROBLEM STOW EVENTS
SELECT to_char(entry_date -8/24, 'DD-MON-YYYY HH12:MI:SSam'), OLD_BIN_ID old_bin, NEW_BIN_ID NEW_BIN, ISBN ASIN, QUANTITY
FROM BINEDIT_ENTRIES
WHERE ENTRY_DATE BETWEEN trunc(SYSDATE) -1 +16/24 AND trunc(SYSDATE) -1 +24/24
--where entry_date BETWEEN trunc(sysdate)-1 '00:00:00' AND trunc(sysdate)-1 '00:00:00.000'
AND substr(old_bin_id,1,2) = 'SC'
AND substr(new_bin_id,1,2) = 'vt'
GROUP BY ENTRY_DATE, OLD_BIN_ID, NEW_BIN_ID, ISBN, Quantity
ORDER BY QUANTITY DESC;
Result:
As you can see it doesn't appear to be looking at the where clause, I believe I have it formatted incorrectly, I typically just look at yesterday as a whole, and not a time range, so this is my first time attempting this. Thank you.

Effectively you're asking for everything between 8 AM and 4 PM local time. I say 8 AM since you're adding 16 hours in the WHERE clause and subtracting 8 in the SELECT clause.
If you meant to query between 7 PM local time and 3AM you would just add 8 hours in the WHERE clause:
WHERE ENTRY_DATE BETWEEN
trunc(SYSDATE) -1 +19/24 + 8/24
AND trunc(SYSDATE) -1 +27/24 + 8/24

Related

Oracle Count number of records each hour [duplicate]

This question already has answers here:
Counting number of records hour by hour between two dates in oracle
(5 answers)
Closed 8 years ago.
I'm trying to get the number of records created each hour but running into trouble with getting the results to group correctly. The idea is similiar to: How to count number of records per day?
However, the field I'm using to Group by is a Date-time field that records down to the second. This seems to be causing trouble with the Group By statement, as when the query returns, there is one row for each second in the specified time period, which is way too much data and will make the work I want to do with the results more difficult than it needs to be (if for no other reason that it's too many rows to fit on one Excel sheet).
My current code is:
SELECT ASD, Count(ASD) Num_CR
From DB_Name.Table_Name fcr
Where trunc(fcr.ASD) > to_Char('31-DEC-2014')
And trunc(fcr.ASD) < to_Char('31-JAN-2015')
And fcr.Status_Code = 'C'
Group By ASD
Order By ASD;
I've tried changing the Group By to be trunc(ASD), but that results in Toad throwing this error: ORA-00979: not a GROUP BY expression.
Thanks in advance!
When you use aggregation anything in the select and order by clauses must match what's in the group by clause:
SELECT trunc(ASD,'hh'), Count(ASD) Num_CR
From DB_Name.Table_Name fcr
Where trunc(fcr.ASD) > to_date('31-DEC-2014')
And trunc(fcr.ASD) < to_date('31-JAN-2015')
And fcr.Status_Code = 'C'
Group By trunc(ASD,'hh')
Order By trunc(ASD,'hh');
When applied to a date, trunc will truncate to the day. To truncate to a different level, specify the format of the element you'd like to truncate to as the second argument (e.g. 'hh' will truncate to the hour; 'mm' will truncate to the month).
SELECT to_char(ASD,'DD-MM-YYYY HH'), Count(ASD) Num_CR
From DB_Name.Table_Name fcr
Where trunc(fcr.ASD) > to_Char('31-DEC-2014')
And trunc(fcr.ASD) < to_Char('31-JAN-2015')
And fcr.Status_Code = 'C'
Group By to_char(ASD,'DD-MM-YYYY HH')
Order By to_char(ASD,'DD-MM-YYYY HH');
Quick and dirty :)
First off, why are you doing to_char on something that's already a string? Secondly, why are you trying to compare something that's (presumably) a DATE column to a string? That way lies madness...
I think you're after something like:
SELECT trunc(ASD, 'hh') asd_hr, Count(ASD) Num_CR
From DB_Name.Table_Name fcr
Where trunc(fcr.ASD) > to_date('31/12/2014', 'dd/mm/yyyy')
And trunc(fcr.ASD) < to_date('31/01/2015', 'dd/mm/yyyy')
And fcr.Status_Code = 'C'
Group By trunc(ASD, 'hh')
Order By trunc(ASD, 'hh');
Also worth noting, did you mean to exclude the last day of January from your query? If so, then fine, but if not, then perhaps you should change it to to_date('01/02/2015', 'dd/mm/yyyy')

SQL count for the current 7 days

I want to get a list of the number of orders entered to our database for the last 7 days. I've tried repeating the below but it becomes some error messages about incorrect format, so I'm wondering what's the correct method.
To get order amount for current day I use SELECT COUNT(order_number) FROM orders WHERE created = TO_CHAR(sysdate, 'DD-MON-YYYY');
I want a table like:
Date Total Orders
sysdate 500
sysdate-1 400
sysdate-2 300
etc. for the last 7 days. Is it possible?
I believe the following would work for you:
Select created, count(ordernumber)
From orders
Where to_date(created, 'DD-MON-YYYY') between trunc(sysdate-7) and trunc(sysdate)
Group by created
Order by to_date(created, 'DD-MON-YYYY') desc
Note, if you can change it I would seriously consider using a DATE column for created.
You should have mentioned the specific error. I guess created is a date field and you are trying to compare it with a string. If this is the case, here is the statement needed. TRUNC removes the time part from the date.
SELECT TRUNC(created), COUNT(order_number)
FROM orders
WHERE TRUNC(created) BETWEEN TRUNC(sysdate) - 6 AND TRUNC(sysdate)
GROUP BY TRUNC(created)
ORDER BY TRUNC(created);

SQL sum 2 different column by different condtion then subtraction and add

what I am trying is kind of complex, I will try my best to explain.
I achieved the first part which is to sum the column by hours.
example
ID TIMESTAMP CUSTAFFECTED
1 10-01-2013 01:00:23 23
2 10-01-2013 03:00:23 55
3 10-01-2013 05:00:23 2369
4 10-01-2013 04:00:23 12
5 10-01-2013 01:00:23 1
6 10-01-2013 12:00:23 99
7 10-01-2013 01:00:23 22
8 10-01-2013 02:00:23 3
output would be
Hour TotalCALLS CUSTAFFECTED
10/1/2013 01:00 3 46
10/1/2013 02:00 1 3
10/1/2013 03:00 1 55
10/1/2013 04:00 1 12
10/1/2013 05:00 1 2369
10/1/2013 12:00 1 99
Query
SELECT TRUNC(STARTDATETIME, 'HH24') AS hour,
COUNT(*) AS TotalCalls,
sum(CUSTAFFECTED) AS CUSTAFFECTED
FROM some_table
where STARTDATETIME >= To_Date('09-12-2013 00:00:00','MM-DD-YYYY HH24:MI:SS') and
STARTDATETIME <= To_Date('09-13-2013 00:00:00','MM-DD-YYYY HH24:MI:SS') and
GROUP BY TRUNC(STARTDATETIME, 'HH')
what I need
what I need sum 2 queries and group by timestamp/hour. 2nd query is exactly same as first but just the where clause is different.
2nd query
SELECT TRUNC(RESTOREDDATETIME , 'HH24') AS hour,
COUNT(*) AS TotalCalls,
SUM(CUSTAFFECTED) AS CUSTRESTORED
FROM some_table
where RESTOREDDATETIME >= To_Date('09-12-2013 00:00:00','MM-DD-YYYY HH24:MI:SS') and
RESTOREDDATETIME <= To_Date('09-13-2013 00:00:00','MM-DD-YYYY HH24:MI:SS')
GROUP BY TRUNC(RESTOREDDATETIME , 'HH24')
so I need to subtract custaffected - custrestoed, and display tht total.
I added link to excel file. http://goo.gl/ioo9hg
Thanks
Ok, now that correct sql is in question text, try this:
SELECT TRUNC(STARTDATETIME, 'HH24') AS hour,
COUNT(*) AS TotalCalls,
Sum(case when RESTOREDDATETIME is null Then 0 else 1 end) RestoredCount,
Sum(CUSTAFFECTED) as CUSTAFFECTED,
Sum(case when RESTOREDDATETIME is null Then 0 else CUSTAFFECTED end) CustRestored,
SUM(CUSTAFFECTED) -
Sum(case when RESTOREDDATETIME is null Then 0 else CUSTAFFECTED end) AS CUSTNotRestored
FROM some_table
where STARTDATETIME >= To_Date('09-12-2013 00:00:00','MM-DD-YYYY HH24:MI:SS')
and STARTDATETIME <= To_Date('09-13-2013 00:00:00','MM-DD-YYYY HH24:MI:SS')
GROUP BY TRUNC(STARTDATETIME, 'HH24')
I recently needed to do this and had to play with it some to get it to work.
The challenge is to get the results of one query to link over to another query all inside the same query and then manipulate the returned value of a field so that the value in a given field in one query's resultset, call it FieldA, is subtracted from the value in a field in a different resultset, call it FieldB. It doesn't matter if the subject values are the result of an aggregation function like COUNT(...); they could be any numeric field in a resultset needing grouping or not. Looking at values from aggregation functions just means you need to adjust your query logic to use GROUP BY for the proper fields. The approach requires creating in-line views in the query and using those as the source of data for doing the subtraction.
A red herring when dealing with this kind of thing is the MINUS operator (assuming you are using an Oracle database) but that will not work since MINUS is not about subtracting values inside a resultset's field values from one another, but subtracting one set of matching records found in another set of records from the final result set returned from the query. In addition, MINUS is not a SQL standard operator so your database probably won't support it if it isn't Oracle you are using. Still, it's awfully nice to have around when you need it.
OK, enough prelude. Here's the query form you will want to use, taking for example a date range we want grouped by YYYY-MM:
select inlineview1.year_mon, (inlineview1.CNT - inlineview2.CNT) as finalcnt from
(SELECT TO_CHAR(*date_field*, 'YYYY-MM') AS year_mon, count(*any_field_name*) as CNT
FROM *schemaname.tablename*
WHERE *date_field* > TO_DATE('*{a year}-{a month}-{a day}*', 'YYYY-MM-DD') and
*date_field* < TO_DATE('*{a year}-{a month}-{a day}*', 'YYYY-MM-DD') and
*another_field* = *{value_of_some_kind}* -- ... etc. ...
GROUP BY TO_CHAR(*date_field*, 'YYYY-MM')) inlineview1,
(SELECT TO_CHAR(*date_field*, 'YYYY-MM') AS year_mon, count(*any_field_name*) as CNT
FROM *schemaname.tablename*
WHERE *date_field* > TO_DATE('*{a year}-{a month}-{a day}*', 'YYYY-MM-DD') and
*date_field* < TO_DATE('*{a year}-{a month}-{a day}*', 'YYYY-MM-DD') and
*another_field* = *{value_of_some_kind}* -- ... etc. ...
GROUP BY TO_CHAR(*date_field*, 'YYYY-MM')) inlineview2
WHERE
inlineview1.year_mon = inlineview2.year_mon
order by *either or any of the final resultset's fields* -- optional
A bit less abstractly, an example wherein a bookseller wants to see the net number of books that were sold in any given month in 2013. To do this, the seller must subtract the number of books retruned for refund from the number sold. He does not care when the book was sold, as he feels a returned book represents a loss of a sale and income statistically no matter when it occurs vs. when the book was sold. Example:
select bookssold.year_mon, (bookssold.CNT - booksreturned.CNT) as netsalescount from
(SELECT TO_CHAR(SALE_DATE, 'YYYY-MM') AS year_mon, count(TITLE) as CNT
FROM RETAILOPS.ACTIVITY
WHERE SALE_DATE > TO_DATE('2012-12-31', 'YYYY-MM-DD') and
SALE_DATE < TO_DATE('2014-01-01', 'YYYY-MM-DD') and
OPERATION = 'sale'
GROUP BY TO_CHAR(SALE_DATE, 'YYYY-MM')) bookssold,
(SELECT TO_CHAR(SALE_DATE, 'YYYY-MM') AS year_mon, count(TITLE) as CNT
FROM RETAILOPS.ACTIVITY
WHERE SALE_DATE > TO_DATE('2012-12-31', 'YYYY-MM-DD') and
SALE_DATE < TO_DATE('2014-01-01', 'YYYY-MM-DD') and
OPERATION = 'return'
GROUP BY TO_CHAR(SALE_DATE, 'YYYY-MM')) booksreturned
WHERE
bookssold.year_mon = booksreturned.year_mon
order by bookssold.year_mon desc
Note that to be sure the query returns as expected, the two in-line views must be equijoined based as shown above on some criteria, as in:
bookssold.year_mon = booksreturned.year_mon
or the subtraction of the counted records can't be done on a 1:1 basis, as the query parser will not know which of the records returned with a grouped count value is to be subtracted from which. Failing to specifiy an equijoin condition will yield a Cartesian join result, probably not what you want (though you may inded want that). For example, adding 'booksreturned.year_mon' right after 'bookssold.year_mon' to the returned fields list in the top-level select statement in the above example and eliminating the
bookssold.year_mon = booksreturned.year_mon
criteria in its WHERE clause will produce a working query that does the subtraction calculation on the CNT values for the YYYY-MM values in the first two columns of the resultset and shows them in the third column. Handy to know this if you need it, as it has solid application in business trends analysis if you can compare sales and returns not just within a given atomic timeframe but as compared across such timeframes in a 1:N fashion.

How to get a SUM of a DATEDIFF but provide cut-off at 24 hours IF a single day is specified

This is actually my first question on stackoverflow, so I sincerely apologize if I am confusing or unclear.
That being said, here is my issue:
I work at a car manufacturing company and we have recently implemented the ability to track when our machines are idle. This is done by assessing the start and end time of the event called "idle_start."
Right now, I am trying to get the SUM of how long a machine is idle. Now, I figured this out BUT, some of the idle_times are LONGER than 24 hours.
So, when I specify that I only want to see the idle_time sums of ONE particular day, the sum is also counting the idle time past 24 hours.
I want to provide the option of CUTTING OFF at that 24 hours. Is this possible?
Here is the query:
{code}
SELECT r.`name` 'Producer'
, m.`name` 'Manufacturer'
-- , timediff(re.time_end, re.time_start) 'Idle Time Length'
, SEC_TO_TIME(SUM((TIME_TO_SEC(TIMEDIFF(re.time_end, re.time_start))))) 'Total Time'
, (SUM((TIME_TO_SEC(TIMEDIFF(re.time_end, re.time_start)))))/3600 'Total Time in Hours'
, (((SUM((TIME_TO_SEC(TIMEDIFF(re.time_end, re.time_start)))))/3600))/((IF(r.resource_status_id = 2, COUNT(r.resource_id), NULL))*24) 'Percent Machine is Idle divided by Machine Hours'
FROM resource_event re
JOIN resource_event_type ret
ON re.resource_event_type_id = ret.resource_event_type_id
JOIN resource_event_type reep
ON ret.parent_resource_event_type_id = reep.resource_event_type_id
JOIN resource r
ON r.`resource_id` = re.`resource_id`
JOIN manufacturer m
ON m.`manufacturer_id` = r.`manufacturer_id`
WHERE re.`resource_event_type_id` = 19
AND ret.`parent_resource_event_type_id` = 3
AND DATE_FORMAT(re.time_start, '%Y-%m-%d') >= '2013-08-12'
AND DATE_FORMAT(re.time_start, '%Y-%m-%d') <= '2013-08-18'
-- AND re.`resource_id` = 8
AND "Idle Time Length" IS NOT NULL
AND r.manufacturer_id = 13
AND r.resource_status_id = 2
GROUP BY 1, 2
Feel free to ignore the dash marks up top. And please tell me if I can be more specific as to figure this out easier and provide less headaches for those willing to help me out.
Thank you so much!
You'll want a conditional SUM, using CASE.
Not sure of syntax for your db exactly, but something like:
, SUM (CASE WHEN TIME_TO_SEC(TIMEDIFF(re.time_end, re.time_start))/3600 > 24 THEN 0
ELSE TIME_TO_SEC(TIMEDIFF(re.time_end, re.time_start))/3600
END)'Total Time in Hours'
This is not an attempt to answer your question. It's being presented as an answer rather than a comment for better formatting and readability.
You have this
AND DATE_FORMAT(re.time_start, '%Y-%m-%d') >= '2013-08-12'
AND DATE_FORMAT(re.time_start, '%Y-%m-%d') <= '2013-08-18'
in your where clause. Using functions like this make your query take longer to execute, especially on indexed fields. Something like this would run quicker.
AND re.time_start >= a date value goes here
AND re.time_start <= another date value goes here
Do you want to cut off when start/end are before/after your time range?
You can use a case to adjust it based on your timeframe, e.g. for time_start
case
when re.time_start < timestamp '2013-08-12 00:00:00'
then timestamp '2013-08-12 00:00:00'
else re.time_start
end
similar for time_end and then use those CASEs within your TIMEDIFF.
Btw, your where-condition for a given date range should be:
where time_start < timestamp '2013-08-19 00:00:00'
and time_end >= timestamp '2013-08-12 00:00:00'
This will return all idle times between 2013-08-12 and 2013-08-18

SQL statement to collect data in a day-by-day, hour-for-hour fashion

I have a database which gets updated with 200-1000 new rows per day. Now, I'd like to have an SQL-statement which returns the data day-by-day, hour-by-hour so I can give a rough estimate for the current trend, i.e. how many rows will be added to the database today, just by taking a quick look at those historical graphs.
So, say that I would like to have 10 graphs printed out for the last 10 days, with the data summed up for every hour, like:
Day9:21,24,15,18,...,30,28,25 : tot 348 (number of rows per hour for day 9 and the total)
Day8:32,37,38,43,...,45,55,65 : tot 442 (number of rows per hour for day 8 and the total)
...
...
Day0:18,25,28,X,Y... : tot 'S' (stats for today so far. What will S be?)
How would the SQL-statement look like to collect the data in this day-by-day, hour-for-hour fashion?
Instead of looking at the graps visually in order to give a rough estimate of today's total 'S', even better would be to compute a prediction of 'S'. But that would be a completely other problem I guess... Any tips for how to do that or hints where I can get more information would be much appreciated!
Thanks,
/Tommy
Hum, depending on your database engine, you'll get different results, but with PostgreSQL, I would do something like that :
SELECT date_trunc('hour', table.date), count(table.id)
FROM table
GROUP BY date_trunc('hour', table.date)
ORDER BY date_trunc('hour', table.date)
The date_trunc function truncates a timestamp field up to a certain point. That query would return you hour by hour, the number of queries, you would just have to do the sums in your software.
If you really mean to have a SQL query returning exactly what you want, I think you'll have to make a function returning a sql set with the proper data, but I think it's easier to do it in your code.
MySQL has a bunch of date/time functions... you might be looking for HOUR(date) as an equivalent to date_trunc('hour', date) in PostGreSQL.
So, if you wanted by Day and by Hour...
SELECT Day(theDate), Hour(theDate), COUNT(1)
FROM theTable
WHERE ....
GROUP BY Day(theDate), Hour(theHour)
ORDER BY Day(theDate), Hour(theHour)
It would give you rows like this:
Day,Hour,Count
1,0,102
1,1,133
...
10,22,47
10,23,384
I had a similar situation, using Oracle. With a table named reporting_data, I wanted a query that could tell me how many records had been inserted per hour, and how many had been inserted in 10 minute increments.
Per hour was easy:
SELECT TO_CHAR(TRUNC(r.creation_date, 'HH'), 'DD-MON-YYYY HH24:MI:SS'),
COUNT (*)
FROM reporting_data r
WHERE r.creation_date > TO_DATE ('27-OCT-2008', 'dd - mon - yyyy')
AND r.creation_date < TO_DATE ('28-OCT-2008', 'dd - mon - yyyy')
GROUP BY TO_CHAR (TRUNC (r.creation_date, 'HH'), 'DD-MON-YYYY HH24:MI:SS')
ORDER BY TO_CHAR (TRUNC (r.creation_date, 'HH'), 'DD-MON-YYYY HH24:MI:SS') ASC
That query would return counts all of the records between Oct 27 and Oct 28, broken down per hour, based on the creation_date column.
Breaking it down in 10 minute increments, instead of hourly increments, was a bit harder, but with some manipulation it was doable.
SELECT SUBSTR(TO_CHAR(r.creation_date, 'DD-MON-YYYY HH24:MI:SS'), 1, 16) || '0:00',
COUNT (*)
FROM reporting_data r
WHERE r.creation_date > TO_DATE ('27-OCT-2008', 'DD-MON-YYYY')
AND r.creation_date < TO_DATE ('28-OCT-2008', 'DD-MON-YYYY')
GROUP BY SUBSTR (TO_CHAR (r.creation_date, 'DD-MON-YYYY HH24:MI:SS'), 1, 16) || '0:00'
There's a lot of string manipulation going on there, so it might not be the most performant way of doing it. On a table of over 25,000,000 rows, it took about a minute to execute. (Then again, just doing a SELECT COUNT(*) on the same table took about 30 seconds, too, so there may have been other issues aside from the query.)