Oracle Total (elapsed) time of all queries of a day - sql

I need to report the total time of all queries executed by a certain module in one day.
-- sum over all queries executed the last 24 hours
select
u.username,
sum(round(v.ELAPSED_TIME / 3600000000 * 60,2)) ELAPSED_TIME_MINUTES,
sum(round(v.ELAPSED_TIME / 3600000000,2)) ELAPSED_TIME_HOURS,
v.module
from gv$sql v
left join dba_users u
on v.parsing_user_id = u.user_id
where u.username like '%XYZ' and module = 'WEBSERVER'
and to_date(v.FIRST_LOAD_TIME,'yyyy-mm-dd hh24:mi:ss') > (trunc(sysdate,'DD') - interval '1' day)
group by username,module;
But I am aksing myself the following:
has v$sql all the queries and how long? it is at least empty after flushing the shared pool
is there a better way to calculate to total time consumed by queries in a certain timeframe?
is FIRST_LOAD_TIME the right field to find the correct day. According to Oracle it is a Timestamp of the parent creation time - should be correct?
Thank you for joining a discussion!

Rows in V$SQL for certain statements disappear after they complete, either due to hints, or due to the nature of the sql statement, such as CREATE TABLE goes away.
If you are setting the MODULE via DBMS_APPLICATION_INFO.SET_MODULE(mod_name, action_name); and have SQL tuning licensed, then just user the Automatic Workload Management (AWR) framework; it includes the MODULE information in the reports it can produce, and gives you number of executions, plus lots of other information.
FIRST_LOAD_TIME could be days ago, or a few moments ago.
I would stay away from V$SQL -- it is too dynamic for what you want to do; and use the SET_MODULE to set the module name, and possibly set a different action name for different parts of your code.

Related

Left Join of two tables based on a range of dates

I am a newbie to SQL coding and am trying to figure out how to create a LEFT JOIN statement based on a date range. The database is analytics from a smartphone app that sends messages to users. The two tables are messageLog (which describes the messages sent to each user) and messageOpenLog (which describes the messages that are opened). Both tables are linked to the message table, but not to each other. To complicate matters, there are a couple other rules we have developed on when messages are able to be sent:
If a message is not opened within 7 days, the message can be resent on day 8.
If a message is opened, then the message can be resent within 60 days.
So, what I want to do is join the two tables together based on the following pseudocode (as I have no idea where to start with actual code):
LEFT JOIN
If (messageOpenLog.DateOpened is within 7 days of messageLog.DateSent)
and messageLog.message_id = messageOpenLog.message_id and
messageLog.user_id = messageOpenLog.user_id
Note: the date format is yyyy-mm-dd hh:mm:ss in both tables.
Any help you can provide would be greatly appreciated.
I am unable to comment on shn's answer, but there is a chance that the user has never opened the message and a messageOpenLog record has not been created. In that case you could add a messageOpenLog.message_id is null to the where clause and get those unopened messages with no corresponding messageOpenLog record as well.
I would suggest:
messagelog ml LEFT JOIN
messageOpenLog mol
ON mol.message_id = ml.message_id AND
mol.user_id = ml.user_id AND
mol.DateOpened >= ml.DateSent AND -- probably not needed
mol.DateOpened < ml.DateSent + interval '7 day'
Note that date arithmetic varies a lot among databases. The exact syntax for adding seven days may be a bit different in your database.
From what I understand from your question and the query below, you are finding all the individual messages that have a time difference of longer than 7 days between their send date and open date.
To do the time difference I would recommend using the DATEDIFF() function that is built into SQL (you may need to format the timestamps into date format with DATE()).
Since the two tables are not directly related you could do something like this:
SELECT
messageOpenLog.*,
messageLog.*
FROM
messageLog LEFT JOIN messageOpenLog ON messageLog.message_id=messageOpenLog.message_id
WHERE
messageLog.user_id = messageOpenLog.user_id AND
DATEDIFF(
day,
DATE(messageLog.timestamp),
DATE(messageOpenLog.timestamp)
) > 7
The structure of this query is dependent on the construction of your tables.
Notice I used the .timestamp column but in your tables this may be named different.
Also I'm not sure if this is actually what you want; if you want to see if the message is more than 7 days old, a different query is required.
Assuming that there is only one messageSent row, this query will get all of the messageOpen rows for the same message that are more than 7 days old.
It's very difficult to give you an exact query based on the information that was presented, such as the potential number of rows with the same message_id, as #amac mentions, there could also be cases where one of the tables has no rows with a certain message_id.

Selecting Between Hours with Timestamp in SQL

I need to figure out how I can select the AVG from another column in a table, between two hour time intervals. I am using PL/SQL/Serverpages or PSP, so the user would select their interval of choice from a drop down menu (ex "2PM-4PM, 4PM-6PM",etc.) and then on the second page, using their choice I will provide information from another column in the table. The issue I have is that the format of my timestamp column is:
30-OCT-16 02.52.00.000000000 PM
30-OCT-16 02.54.00.000000000 PM
The way I have been trying to solve this problem is by using the following methodology:
IF number_text = 1 THEN
SELECT AVG(column) INTO avg_power
FROM table
WHERE date_column BETWEEN TO_DATE('12','HH') AND TO_DATE('2','HH')
AND LIKE '%PM';
I am going to use various IF statements in order to activate each select statement with the IF contingent on which interval the user selects from a drop down list.
As I said, the variable time depends on what the user selects on a prior page. My biggest issues in this situation are figuring out how I am supposed to code the WHERE clause as well as finding a way to work with the data, in terms of hours, as it exists in the database, while also taking AM and PM into account. I greatly appreciate any and all help to solve this issue.

SQL for Next/Prior Business Day from Calendar table (in MS Access)

I have a Calendar table pulled from our mainframe DBs and saved as a local Access table. The table has history back to the 1930s (and I know we use back to the 50s in at least one place), resulting in 31k records. This Calendar table has 3 fields of interest:
Bus_Dt - every day, not just business days. Primary Key
Bus_Day_Ind - indicates if the day was a valid business day for the stock market.
Prir_Bus_Dt - the prior business day. Contains some errors (about 50), all old.
I have written a query to retrieve the first business day on or after the current calendar day, but it runs supremely slowly. (5+ minutes) I have examined the showplan output and see it is being run via an x-join, which between 30k+ record tables gives a solution space (and date comparisons) in the order of nearly 10 million. However, the actual task is not hard, and could be preformed comfortably by excel in minimal time using a simple sort.
My question is thus, is there any way to fix the poor performance of the query, or is this an inherent failing of SQL? (DB2 run on the mainframe also is slow, though not crushingly so. Throwing cycles at the problem and all that.) Secondarily, if I were to trust prir_bus_dt, can I get there better? Or restrict the date range (aka, "cheat"), or any other tricks I didn't think of yet?
SQL:
SELECT TE2Clndr.BUS_DT AS Cal_Dt
, Min(TE2Clndr_1.BUS_DT) AS Next_Bus_Dt
FROM TE2Clndr
, TE2Clndr AS TE2Clndr_1
WHERE TE2Clndr_1.BUS_DAY_IND="Y" AND
TE2Clndr.BUS_DT<=[te2clndr_1].[bus_dt]
GROUP BY TE2Clndr.BUS_DT;
Showplan:
Inputs to Query
Table 'TE2Clndr'
Table 'TE2Clndr'
End inputs to Query
01) Restrict rows of table TE2Clndr
by scanning
testing expression "TE2Clndr_1.BUS_DAY_IND="Y""
store result in temporary table
02) Inner Join table 'TE2Clndr' to result of '01)'
using X-Prod join
then test expression "TE2Clndr.BUS_DT<=[te2clndr_1].[bus_dt]"
03) Group result of '02)'
Again, the question is, can this be made better (faster), or is this already as good as it gets?
I have a new query that is much faster for the same job, but it depends on the prir_bus_dt field (which has some errors). It also isn't great theory since prior business day is not necessarily available on everyone's calendar. So I don't consider this "the" answer, merely an answer.
New query:
SELECT TE2Clndr.BUS_DT as Cal_Dt
, Max(TE2Clndr_1.BUS_DT) AS Next_Bus_Dt
FROM TE2Clndr
INNER JOIN TE2Clndr AS TE2Clndr_1
ON TE2Clndr.PRIR_BUS_DT = TE2Clndr_1.PRIR_BUS_DT
GROUP BY TE2Clndr.BUS_DT;
What about this approach
select min(bus_dt)
from te2Clndr
where bus_dt >= date()
and bus_day_ind = 'Y'
This is my reference for date() representing the current date

Tips on improving calculation speed in Access

I'm using MS Access 2013 (without any access to other SQL programs).
I have to filter data, based on daily breakdowns across 30 years (10800 rows to start with) for more than 50 customers (540.000 rows in total). I have an issue with the speed of my queries. After a few joins and filters the whole thing takes seconds to run. To speed up the calculation I tried:
breaking queries down (using as few joins as possible)
filtering for 'active data' only;
aggregating iteration queries;
indexed the dates (as Integers).
In need to add, for each calendar date, the summed DealValue, aggregated by Customer, for it's 'active period' (each day between the StartDate and EndDate of the given Deal). The deals may spread over 30 years. Looks like that LEFT JOIN is causing all the slow-down.
My final query looks like:
SELECT QMaster.CalDate,
QDeals.CustomerID,
Sum(IIf([QDeals]![Limit]="S",[QDeals.DealValue],0)) AS S,
Sum(IIf([QDeals]![Limit]="L",[QDeals.DealValue],0)) AS L
FROM QMaster
LEFT JOIN TDeals
ON (QMaster.CalDate> QDeals.DealStartDate)
AND (QMaster.CalDate< QDeals.DealEndDate)
GROUP BY QMaster.CalDate, QDeals.CustomerID;
-- where QMaster (540.000 rows) / QDeals (> 10.000 rows) / calculation time 5<>7s
-- the query may contain typos as I had to change the name description for disclosure.
Any tips on how to replace that LEFT JOIN?
Or any other speed improvements prior to this query?
I want to add an 'interactive' Access form over my 'master' query and that's turning to be very slow (not user friendly).

Simultaneous calls from CDR

I need to come up with an analysis of simultaneus events, when having only starttime and duration of each event.
Details
I've a standard CDR call detail record, that contains among others:
calldate (timedate of each call start
duration (int, seconds of call duration)
channel (a string)
What I need to come up with is some sort of analysys of simultaneus calls on each second, for a given timedate period. For example, a graph of simultaneous calls we had yesterday.
(The problem is the same if we have visitors logs with duration on a website and wish to obtain simultaneous clients for a group of web-pages)
What would your algoritm be?
I can iterate over records in the given period, and fill an array, where each bucket of the array corresponds to 1 second in the overall period. This works and seems to be fast, but if the timeperiod is big (say..1 year), I would need lots of memory (3600x24x365x4 bytes ~ 120MB aprox).
This is for a web-based, interactive app, so my memory footprint should be small enough.
Edit
By simultaneous, I mean all calls on a given second. Second would be my minimum unit. I cannot use something bigger (hour for example) becuse all calls during an hour do not need to be held at the same time.
I would implement this on the database. Using a GROUP BY clause with DATEPART, you could get a list of simultaneous calls for whatever time period you wanted, by second, minute, hour, whatever.
On the web side, you would only have to display the histogram that is returned by the query.
#eric-z-beard: I would really like to be able to implement this on the database. I like your proposal, and while it seems to lead to something, I dont quite fully understand it. Could you elaborate? Please recall that each call will span over several seconds, and each second need to count. If using DATEPART (or something like it on MySQL), what second should be used for the GROUP BY. See note on simultaneus.
Elaborating over this, I found a way to solve it using a temporary table. Assuming temp holds all seconds from tStart to tEnd, I could do
SELECT temp.second, count(call.id)
FROM call, temp
WHERE temp.second between (call.start and call.start + call.duration)
GROUP BY temp.second
Then, as suggested, the web app should use this as a histogram.
You can use a static Numbers table for lots of SQL tricks like this. The Numbers table simply contains integers from 0 to n for n like 10000.
Then your temp table never needs to be created, and instead is a subquery like:
SELECT StartTime + Numbers.Number AS Second
FROM Numbers
You can create table 'simultaneous_calls' with 3 fields: yyyymmdd Char(8),
day_second Number, -- second of the day,
count Number -- count of simultaneous calls
Your web service can take 'count' value from this table and make some statistics.
Simultaneous_calls table will be filled by some batch program which will be started every day after end of the day.
Assuming that you use Oracle, the batch may start a PL/SQL procedure which does the following:
Appends table with 24 * 3600 = 86400 records for each second of the day, with default 'count' value = 0.
Defines the 'day_cdrs' cursor for the query:
Select to_char(calldate, 'yyyymmdd') yyyymmdd,
(calldate - trunc(calldate)) * 24 * 3600 starting_second,
duration duration
From cdrs
Where cdrs.calldate >= Trunc(Sysdate -1)
And cdrs.calldate
Iterates the cursor to increment 'count' field for the seconds of the call:
For cdr in day_cdrs
Loop
Update simultaneos_calls
Set count = count + 1
Where yyyymmdd = cdr.yyyymmdd
And day_second Between cdr.starting_second And cdr.starting_second + cdr.duration;
End Loop;