I have 2 cursors inside my procedure for which i want to use single for loop because using this i want to loop 2 variable inside single select query. The two variable has 2 different values which is used inside single select query. These 2 values are coming from KPI_DEFINITION table which gives me timestamp which i want to compare in my select query for data extraction. The first column KPI_FREQUENCY has value for example 30 and KPI_FREQ_TIME_UNIT column has value MINUTE. So if we combine these 2 column we will get 30 MINUTE and there is another combined column value which is 50 MINUTE and there might be more. So thats why i want to put this inside loop and compare in my select query with the start_date field value.But dont know how to do that. I simply use 2 cursors to take this 2 column and trying to loop it inside single for loop but dont know how to do that.There are might be another solution for this if i dont want to use two cursors but did not find a way.
create or replace PROCEDURE "EXT_TEST" AS
LAST_WF_ID Number := 0;
LAST_UNIT NUMBER:=10;
LAST_UNIT_VAL VARCHAR2(20);
CURSOR KPI_FREQUENCY_CUR IS
Select KPI_FREQUENCY INTO LAST_UNIT from RATOR_MONITORING_CONFIGURATION.KPI_DEFINITION WHERE
EVENT_ID = 10028;
CURSOR KPI_FREQ_TIME_UNIT_CUR IS
Select KPI_FREQ_TIME_UNIT INTO LAST_UNIT_VAL from RATOR_MONITORING_CONFIGURATION.KPI_DEFINITION WHERE
EVENT_ID = 10028;
BEGIN
DBMS_OUTPUT.PUT_LINE('LAST_UNIT - ' || LAST_UNIT);
DBMS_OUTPUT.PUT_LINE('LAST_UNIT_VAL - ' || LAST_UNIT_VAL);
select MIN(ID) INTO LAST_WF_ID from WF_WORKFLOW#FONIC_RETAIL where start_date > sysdate - numtodsinterval(LAST_UNIT,LAST_UNIT_VAL);
DBMS_OUTPUT.PUT_LINE('LAST_WF_ID - ' || LAST_WF_ID);
END EXT_TEST;
Sample data from KPI_DEFINITION table:
KPI_DEF_ID KPI_FREQUENCY KPI_FREQ_TIME_UNIT EVENT_ID
1000136 30 MINUTE 10028
1000137 50 MINUTE 10028
Pending seeing what data your actually on .. I'd suggest trying something a lot simpler ... such as this:
select r.kpi_frequency, r.kpi_freq_time_unit, min(f.id)
from rator_monitoring_configuration.kpi_definition r,
wf_workflow#fonic_retail f
where r.event_id = 10028
and f.start_date > sysdate - numtodsinterval ( r.kpi_frequency, r.kpi_freq_time_unit );
group by r.kpi_frequency, r.kpi_freq_time_unit
order by r.kpi_frequency, r.kpi_freq_time_unit;
Related
I have a table called Nums in which I have just one column called text. I need to create a PSQL query that will insert a row for each number between 100000 and 199999.
Row number is 100000
Row number is 100001
Row number is 100002
...
Row number is 199999
Obviously, if the range was smaller and only 10 numbers, it could be done with 10 simple insert statements, but this is not the case. I'm new to PSQL and want to know how this could be achived? Is a sort of loop needed?
You can use recursive queries.
with recursive cte as
(
select 100000 as n
union all
select n + 1
from cte
where n < 199999
)
select * from cte;
Try this dbfiddle
Assuming you mean "Postgres" for the database, use generate_series():
insert into nums (text)
select n
from generate_series(100000, 199999, 1) gs(n);
Postgres will automatically convert n to a string -- although it seems unusual to store numbers as strings. You can also be explicit using select n::text.
EDIT:
If you need the full string, it would just look like:
insert into nums (text)
select 'Row number is ' || (n::text)
from generate_series(100000, 199999, 1) gs(n);
I want to take a value from a selected column to operate the next column. For example:
SELECT CASE
WHEN ID < 4 THEN ID
ELSE 10
END
AS MY_ID,
MY_ID + 5 AS EXTRA_ID
FROM FOO
That would output for IDs 1,2,3,4,5:
MY_ID EXTRA_ID
1 6
2 7
3 8
10 15
10 15
If I do MY_ID + 5 it will complain about MY_ID not existing (it's an alias, so it makes sense) and ID + 5 will read 1+5, 2+5, 3+5, 4+5, 5+5 instead of 1+5, 2+5, 3+5, 4+10, 5+10 when it goes through the ELSE. Is it even possible to do this? I'm doing it in SSRS - Report builder, and need to operate a result that might be set to a defualt value depending on the CASE clause.
You can repeat the same CASE expression with +5 in the end for the extra_id column
SELECT CASE
WHEN ID < 4 THEN ID
ELSE 10
END
AS MY_ID,
CASE
WHEN ID < 4 THEN ID
ELSE 10
END + 5 AS EXTRA_ID
FROM FOO
An alternative is to create the extra_id column value inside SSRS using an expression
= Fields!my_id.value + 5
you cannot reuse the calculation in the same level. Using my_id in the where clause will fail as well. Either you have to calucate it multiple times, place another select around your statement or use a with statement (CTE).
Simply wrap it with another select:
SELECT t.*,
t.my_id + 5 as extra_id
FROM(Your Query) t
Derived columns are not available in the same layer they're being created. By wrapping them with another select, you make them available (that because the inner query is being processed before the outer) .
You just need a subquery to create MY_ID before doing anything with it. By creating MY_ID in the inner query the outer query can use to define new fields.
SELECT
a.MY_ID,
a.MY_ID + 5 AS EXTRA_ID
from
(SELECT
CASE
WHEN ID < 4 THEN ID
ELSE 10
END
AS MY_ID
FROM FOO) as a
It is given the following scenario. I have a list of 3000 first names and a list of 2500 last names. Each one of them has a "ranking" that represents the position in a name's top. Two or more names can have the same ranking. Also, a table with 1500 cities is given, each with 4 census values in certain years.
From the tables above I must generate 5 million random entries containing the first name, last name, birth date and place of birth of one person, that should follow the rules given by ranking of the names and population number of the cities.
This have to be generated using just Oracle (stored functions, stored procedures and so on). How can I do this?
Disclaimer: I'm not a statistics expert, and there are probably way more efficient means to do that.
The most challenging task seems to be the creation of 5 million names according to ranks. In real world, those would be distributed unevenly among the population: difference between second last and last would be 1-2 persons, and the difference between the first and second rank could be thousands of people. That said, I have no idea how to achieve that, so we'll model it in other way. Suppose we have total population of 100 and list of four ranked names:
Alice: 1
Bob: 2
Betty: 2
Claire: 3
We can make the distribution "even", so that rank 3 has X people, rank 2 has twice as many, and rank 1 thrice as many. If the ranks were unique, the formula would be as simple as X + 2X + 3X = 100, but we have two names in rank 2, so it should be X + 2*2X + 3X = 100, so X = 12.5. We can truncate it to integer and get people counts for all ranks except the first (12, 24 and 24) and first rank would get what remains: 40. Seems good enough, though it will not work for edge case when you have multiple first ranks.
There's a little problem, though. For 3000 different names, the sum of coefficients would be 4501500. So, truncated X would be 1, making rank 3000 to rank 2 have 1 to 2999 people respectively, and rank 1 have a little under 500000. That's not quite good enough. To illustrate with four names above, assume total count of 15. With current algorithm, X will be 1 as well, and distribution will be 1-2-2-10. Luckily, we'll be processing ranks one by one in procedure, so we can remove processed people from equation and recalculate X. E.G. first it's X + 2*2X + 3X = 15 with X=1, then 2*2X + 3X = 14 with X=2. This way, distribution will be 1-4-4-6, which is far from ideal, but better.
Now, this can already be expressed as PL/SQL. I suggest to create the table with following columns: LAST_NAME, FIRST_NAME, BIRTHDAY, CITY, RAND_ROWNO.
First of all, let's fill it with 5M last names. Assuming your table for them is last_names(name, name_rank), you'll need the following:
declare
cursor cur_last_name_ranks is
select name_rank, count(*) cnt, row_number() over (order by name_rank desc) coeff
from last_names l
group by name_rank;
cursor cur_last_names (c_rank number) is
select name from last_names
where name_rank = c_rank;
v_coeff_sum number;
v_total_people_count number:= 5000000;
v_remaining_people number;
v_x number;
v_insert_cnt number;
begin
--Get a sum of all coefficients for our formula
select sum(coeff) into v_coeff_sum
from
(
select count(*) * row_number() over (order by name_rank desc) coeff
from last_names l
group by name_rank
);
v_remaining_people := v_total_people_count;
--Now, loop for all coefficients
for r in cur_last_name_ranks loop
--Recalculate X
v_x := trunc(v_remaining_people / v_coeff_sum);
--First, determine how many rows should be inserted per last name with such rank
if r.name_rank = 1 then
if r.cnt > 1 then
--raise an exception here, we don't allow multiple first ranks
raise TOO_MANY_ROWS;
end if;
v_insert_cnt := v_remaining_people;
else
v_insert_cnt := v_x*r.coeff;
end if;
--Insert last names N times.
--Instead of multiple INSERT statements, use select from dual with connect trick.
for n in cur_last_names(r.name_rank) loop
insert into result_table(last_name)
select n.name from dual connect by level <= v_insert_cnt;
end loop;
commit;
--Calculate remaining people count
v_remaining_people := v_remaining_people - v_x*r.cnt*r.coeff;
--Recalculate remmaining coefficients
v_coeff_sum := v_coeff_sum - r.cnt*r.coeff;
end loop;
end;
Now you have 5 million rows with last names filled according to ranks. Now, we'll need to assign random number from 1 to 5000000 for each row - you'll see why. This is done with a single query using merge on self:
merge into result_table t1
using (select rowid rid, row_number() over (ORDER BY DBMS_RANDOM.VALUE) rnk from result_table) t2
on (t1.rowid = t2.rid)
when matched then update set t1.rand_rowno = t2.rnk
Note that it will take some time because of large size.
Now you must repeat the same procedure for first names. It'll be very similar to last names, except you'll be updating existing records, not inserting new. If you keep track of how many rows you've updated already, it'll be as simple putting this in the inner loop:
update result_table
set first_name = n.name
where rand_rowno between
(v_processed_rows+1) and
(v_processed_rows+v_insert_cnt);
v_processed_rows := v_processed_rows+v_insert_cnt;
That does it - you now have a decent sample of 5M names according to your ranking, last names randomly matched with first names.
Now, for census. I don't really understand your format, but that's relatively simple. If you get data to the form of "N people were born in city C between DATE1 and DATE2", you can update the table in a loop, setting N rows to have CITY = C and BIRTHDAY = a random date between DATE1 and DATE2. You'll need a function to return a random date from a time period, see this. Also, don't forget to assign random row numbers again before doing that.
I'll leave the census part for you to implement, I've spent too much time on writing this already. Thanks for a good brain exercise!
I am trying to write a query that will output the following table:
|Day_0_Revenue|Day_1_Revenue|Day_2_Revenue|....|Day_90_Revenue|
The data looks like the following table. Each day will have multiple values, and I want to sum the revenue for each day. Day 0 is 11/1.
|Date|Revenue|
11/1 5
11/2 3
11/3 5
11/3 7
11/4 8
11/5 8
11/5 12
11/6 7
I believe this is simple to do if the table I want is vertical. The reason I want to output horizontally is because I will fill the table vertically with other dates. My main question is how to increment the day value without writing a really long SELECT clause? I'm not sure if I can write a loop that will have something like Day+1 until Day=90...
This is not possible in SQLite alone.
The easiest way would be to just use GROUP BY day to create a vertically oriented table, and then reorder the cells when you are displaying them.
If you really must get a horizontally oriented table from the database, you have to create the query dynamically:
SELECT (SELECT SUM(Revenue) FROM MyTable WHERE Date = '11/1') AS Day_0_Revenue,
(SELECT SUM(Revenue) FROM MyTable WHERE Date = '11/2') AS Day_1_Revenue,
(SELECT SUM(Revenue) FROM MyTable WHERE Date = '11/3') AS Day_2_Revenue,
...
You have to do this in whatever programming language you are using to access the database:
sql = "SELECT"
for i in range(90):
sql += " (SELECT SUM(Revenue) FROM MyTable" +
" WHERE Date = '%s') AS Day_%d_Revenue," % (dates[i], i)
sql = sql[:-1] # remove last comma
cursor = db.execute(sql)
Let's say, we have this table:
STUDENT | START | END
1 |1998-1-1 |2001-1-1
2 |1999-1-1 |2001-1-1
3 |2000-1-1 |2004-1-1
4 |2000-1-1 | NULL
I'm trying to do is:
Count number of students between start and end dates!
Looks like you need to use a basic COUNT aggregate:
SELECT COUNT(Student)
FROM YourTable
WHERE Start >= #Start
AND End <= #End
I've used >= and <= respectively around the start and end date fields. Feel free to change to > or < as needed. It was unclear from your question whether you wanted between a specific field or if you were checking for a range between those two fields.
Use the between Operator and COUNT aggregate function
SELECT COUNT(student) column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
Between can be used with text so insert the dates where the values are,
Read more here if you still don't understand
EDIT : That should work, sorry about the error
http://www.w3schools.com/sql/sql_between.asp