Orace/SQL multiple group by - sql

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;

Related

Discrepancy in counts - Oracle SQL group by query

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.

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.

Find out whether values exists in other table within time range

I'm trying to evaluate whether a user's action is performed in relativity to another action. Both actions are marked with a timestamp.
What I want to evaluate is whether a user when she has an entry in one table e.g. A (both tables has the same ID for an individual user) has an entry in another table, B, in between the time of entry in A and 5 minutes earlier. I manage to extract time for all values
SELECT
TO_CHAR(playdatetime - 5/(24*60),'YYYY-MM-DD HH24:MI:SS') five_min_future,
TO_CHAR(playdatetime,'YYYY-MM-DD HH24:MI:SS') jetzt,
TO_CHAR(SEARCHDATE,'YYYY-MM-DD HH24:MI:SS') ses_time
I tried to make something like this:
CASE
WHEN TO_CHAR(SEARCHDATE,'YYYY-MM-DD HH24:MI:SS')
BETWEEN TO_CHAR(playdatetime - 1/(24*60),'YYYY-MM-DD HH24:MI:SS')
AND
TO_CHAR(playdatetime,'YYYY-MM-DD HH24:MI:SS')
THEN 1
ELSE 0
END timediff
but how could I get this to just check for one user and not just evaluate the row at hand?
The following query shows records that exist in both a and b, where the record in b occurs up to 5 minutes before that in a:
select a.user_id,
a.event_dt,
b.event_dt
from a
join b
on b.user_id = a.user_id
and b.event_dt between (a.event_dt - (5 / (60 * 24))) and a.event_dt;