Discrepancy in counts - Oracle SQL group by query - sql

I am trying to fetch the count of records between 3 PM and 4 PM. The first query returns 473 records whereas the second query returns 474 between 3 PM and 4 PM. Please can you let me know which one could be correct or what I need to do in order to identify the query that provides the accurate result?
select count(*)
from table
where start_ts between
to_timestamp('2017-03-06 15:00:00','YYYY-MM-DD HH24:MI:SS') and to_timestamp('2017-03-06 16:00:00','YYYY-MM-DD HH24:MI:SS')
select count(*),to_char(start_ts,'YYYY-MM-DD HH24')
from table
where start_ts between
to_timestamp('2017-03-06 00:00:00','YYYY-MM-DD HH24:MI:SS') and to_timestamp('2017-03-06 23:59:59','YYYY-MM-DD HH24:MI:SS')
group by to_char(start_ts,'YYYY-MM-DD HH24')
order by to_char(start_ts,'YYYY-MM-DD HH24')

Here is one guess. The first query is return 374 records and the second is returning two rows, one with 373 records and one with 1 record.
The 1 record has a time of exactly '2017-03-06 16:00:00', so it shows up in a different bin from the rest.
This seems like a plausible explanation, although it requires that the numbers be in the other order.

Related

Timestamp filter in PLSQL

Let's say I have an table containing a user rating of movies like IMDB movie database.
ID
USER_REVIEW
USER_RATING
USER_ID
1
blub
10
1
2
blob
9
2
3
blab
7
3
These table inserts have been taken yesterday.
The entries with the id 1 and 2 have been taken between 16:00 and 16:10. The third entry with id 3 have been taken between 16:10 and 16:15 How can I find a select statement that filters me the first two entries between 16:00 and 16:10?
Or is it possible to find out which are the exact timestamps of the inserts from yesterday?
is it possible to find the exact timestamp of the inserts from yesterday?
Sure, if there's a timestamp column in the table.
As it looks as if such a column doesn't exist, a simple option to add it is
alter table movies add date_insert date default sysdate;
Oracle will auto-populate that column with SYSDATE (unless you explicitly insert it), which means that your current INSERT statement remains "as is", you don't have to change it a bit.
Then you'd - for example - select rows inserted yesterday as
select *
from movies
where trunc(date_insert) = trunc(sysdate - 1)
Or, as you asked, rows inserted yesterday (10th of August 2021) between 16:00 and 16:10:
select *
from movies
where date_insert between to_date('10.08.2021 16:00', 'dd.mm.yyyy hh24:mi')
and to_date('10.08.2021 16:10', 'dd.mm.yyyy hh24:mi')
try the GETDATE() function.
here is something to help you:
https://www.mssqltips.com/sqlservertip/1145/date-and-time-conversions-using-sql-server/

Orace/SQL multiple group by

I can't seem to get my head around this...
I have the following statement:
select aT.PieceNo, count(*)
from send_piece aT
where message_no in (7104, 7113)
and aT.Created > To_Timestamp('01-JAN-14 00:00:00', 'DD-MON-YY HH24:MI:SS')
group by aT.PieceNo;
In this grouped list, there will be many records with the same PieceNo AND aT.Created value. I need to show only these records.
I tried
group by aT.PieceNo, aT.Created;
but it seemed to reverse the initial grouping.
Table structured as follows: ID, Message_No, XML_Data, Created
I am extracting the PieceNo value from the XML_Data. The above query returns:
167408305E01 5
167408505C01 8
167408206A01 9
167408306A01 4
...
Each record within that group contains a Created value (DateTime). I need to show only the records that share the same Created value within those groups (if that makes sense?).
Basically, I'm trying to extract all the records in this table that share the same PieceNo and Created values.
select aT.PieceNo, aT.Created,count(*)
from send_piece aT
where message_no in (7104, 7113)
and aT.Created > To_Timestamp('01-JAN-14 00:00:00', 'DD-MON-YY HH24:MI:SS')
group by aT.PieceNo, aT.Created;

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.

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.)