Given the following table
create table tmp_test as
select mod(level, 5) as n
from dual
connect by level <= 10
;
and this function
create or replace function test_deterministic (Pn in number
) return number deterministic is
begin
dbms_output.put_line(Pn);
dbms_lock.sleep(1);
return Pn;
end;
It executes 6 times, taking 6 seconds:
SQL> select test_deterministic(n) from tmp_test;
TEST_DETERMINISTIC(N)
---------------------
1
2
3
4
0
1
2
3
4
0
10 rows selected.
1
2
3
4
0
1
Elapsed: 00:00:06.02
I would have expected this to execute 5 times. If I run this SELECT statement in SQL Developer or PL/SQL Developer it only executes 5 times. Equally, if I run this in Pl/SQL it executes 5 times:
SQL> begin
2 for i in ( select test_deterministic(n) from tmp_test ) loop
3 null;
4 end loop;
5 end;
6 /
1
2
3
4
0
Elapsed: 00:00:05.01
Why is this function executed 6 times when called in SQL from SQL*Plus? I expected it to execute 5 times instead.
I'm on version 11.2.0.3.5 and the SQL*Plus client is release 11.2.0.1.0 (64bit).
Blame SQL*Plus, Ben. Your function works, in this situation, correctly. The extra value(1) you see is there because of arraysize value, and, mostly because of the way how SQL*Plus fetches rows. It first fetches first row and only then it starts to use arraysize for subsequent fetches. Every new fetch is a new database call, which forces your deterministic function to be evaluated. Try to set the arraysize to 1 or 2(same effect) and execute your select statement. The first row returns, and then, arraysize comes to play and each subsequent fetch will return couple of rows:
Arraysize is set to 1(two in fact)
SQL> set arraysize 1;
SQL> select test_deterministic(n) from tmp_test;
TEST_DETERMINISTIC(N)
---------------------
1
2
3
4
0
1
2
3
4
0
10 rows selected.
1
2
3
4
0
1
2
3
4
0
Elapsed: 00:00:10.10
The same query with much larger arraysize:
SQL> set arraysize 50;
SQL> select test_deterministic(n) from tmp_test;
TEST_DETERMINISTIC(N)
---------------------
1
2
3
4
0
1
2
3
4
0
10 rows selected.
1
2
3
4
0
1
Elapsed: 00:00:06.06
SQL> spool off;
Any other client, whether it's SQL Developer or PL/SQL Developer lacks such behavior and gives correct output.
Related
How can I dynamically assign the Column Names in Bigquery Procedure. I have a counter declared in my procedure:
DECLARE counter int64 default 1;
And in the procedure, I use while loop to keep self joining till the time there are no more parents remaining. My only question is to determine a way to find the dynamic column names. like manager_id_{#counter} based on the hierarchy available.
I have the following tables available:
Table 1:
emp_id
manager_id
1
1
2
1
3
2
4
3
10
10
20
10
30
20
My Output:
emp_id
manager_id
manager_id_1
manager_id_2
manager_id_3
manager_id_4
1
1
1
NULL
NULL
NULL
2
1
1
2
NULL
NULL
3
2
1
2
3
NULL
4
3
1
2
3
4
10
10
10
NULL
NULL
NULL
20
10
10
20
NULL
NULL
30
20
10
20
30
NULL
I am able to come up with the logic, the only missing piece is the column name. I saw many posts which were related to SQL and other DBMS. But couldn't find something for BigQuery.
DECLARE counter int64 default 1;
execute immediate "Select " || 5 || " as manager_id_" || counter
The way I resolved it was using Jinja like scripting.
Suppose I have the following records:
code sequence_no group_no
4 1 1
2 2 1
3 3 1
4 4 1
1 1 2
3 1 3
4 2 3
the output should be: within the same group(by group_no) the code column is updated with the first(by sequence_no) code that is not 4.
so the output should look like this:
code sequence_no group_no
2 1 1
2 2 1
2 3 1
2 4 1
1 1 2
3 1 3
3 2 3
Here's my code, and my logic is that if the input.code == 4, then assign the next code to the temp variable. This doesn't work if the first record code == 4 for some reason. And I don't think this logic covers the last record of the group if it == 4.
type temporary_type=
record
decimal("\x01") l_code;
end;
temp :: scan(temp, in) =
begin
temp.l_code::
if (temp.l_code == 4) in.code
else temp.l_code;
end;
temp :: initialize(in) =
begin
temp.code :: in.code;
end;
out :: finalize(temp, in) =
begin
out.* :: in.*;
out.code :: temp.l_code;
end;
I am trying to change something like this:
Index Record Time
1 10 100
1 10 200
1 10 300
1 10 400
1 3 500
1 10 600
1 10 700
2 10 800
2 10 900
2 10 1000
3 5 1100
3 5 1200
3 5 1300
into this:
Index CountSeq Record LastTime
1 4 10 400
1 1 3 500
1 2 10 700
2 3 10 1000
3 3 5 1300
I am trying to apply this logic per unique index -- I just included three indexes to show the outcome.
So for a given index I want to combine them by streaks of the same Record. So notice that the first four entries for Index 1 have Records 10, but it is more succinct to say that there were 4 entries with record 10, ending at time 400. Then I repeat the process going forward, in sequence.
In short I am trying to perform a count-grouping over sequential chunks of the same Record, within each index. In other words I am NOT looking for this:
select index, count(*) as countseq, record, max(time) as lasttime
from Table1
group by index,record
Which combines everything by the same record whereas I want them to be separated by sequence breaks.
Is there a way to do this in SQL?
It's hard to solve your problem without having a single primary key, so I'll assume you have a primary key column that increases each row (primkey). This request would return the same table with a 'diff' column that has value 1 if the previous primkey row has the same index and record as the current one, 0 otherwise :
SELECT *,
IF((SELECT index, record FROM yourTable p2 WHERE p1.primkey = p2.primkey)
= (SELECT index, record FROM yourTable p2 WHERE p1.primkey-1 = p2.primkey), 1, 0) as diff
FROM yourTable p1
If you use a temporary variable that increases each time the IF expression is false, you would get a result like this :
primkey Index Record Time diff
1 1 10 100 1
2 1 10 200 1
3 1 10 300 1
4 1 10 400 1
5 1 3 500 2
6 1 10 600 3
7 1 10 700 3
8 2 10 800 4
9 2 10 900 4
10 2 10 1000 4
11 3 5 1100 5
12 3 5 1200 5
13 3 5 1300 5
Which would solve your problem, you would just add 'diff' to the group by clause.
Unfortunately I can't test it on sqlite, but you should be able to use variables like this.
It's probably a dirty workaround but I couldn't find any better way, hope it helps.
I need to create a stored procedure in SQL Server that accepts the following two parameters:
A select statement returning 1 column.
A number of columns.
The stored procedure would then run the select statement and return the result of the select statement with the values of the single column split into the given amount of columns per row.
Here are some examples:
exec stored_proc ‘select id from table where id between 1 and 20’, 5
The result of the select would be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
The result of the stored procedure call would be:
1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
16 17 18 19 20
Or the call could be:
exec stored_proc ‘select id from table where id between 1 and 20’, 10
Giving the result of:
1 2 3 4 5 6 7 8 9 10
11 12 13 14 15 16 17 18 19 20
Though I'm not sure you should be doing this in SQL, it can be done.
I think the way to do it would be do create a cursor and use it's iterations to build a dynamic SQL statement.
During each iteration, add each piece of data as a new column (field) and when you reach the number of columns add something like Union Select
I have a table
WS_ID WS_WEBPAGE_ID WS_SPONSORS_ID WS_STATUS WS_CREATEDTS
2 3 2 N 2012-06-07 15:32:00
3 3 3 N 2012-06-07 15:37:00
4 3 4 Y 2012-06-07 15:41:00
5 1 1 Y 2012-06-07 16:05:00
6 2 1 Y 2012-06-07 16:05:00
7 2 4 Y 2012-06-07 16:05:00
8 4 1 Y 2012-06-07 16:05:00
9 1 3 Y 2012-06-07 16:05:00
10 1 2 Y 2012-06-07 16:05:00
11 1 4 Y 2012-06-07 16:05:00
12 2 3 Y 2012-06-07 16:05:00
13 2 3 Y 2012-06-07 16:05:00
14 2 1 Y 2012-06-07 16:05:00
24706 3 8,7,9,5,5 NULL NULL
I wrote a cursor and calling the split function in to that cursor to split the last record
Declare #splitc varchar(250)
Declare splitcursor cursor for
select ws_sponsors_id from dbo.TBL_WD_SPONSORS
open splitcursor
fetch next from splitcursor into #splitc
while(##FETCH_STATUS = 0)
begin
print '#splitcursor'
--select * from dbo.Comma_Split(#ws_sponsors_id,',')
select dbo.Comma_Split(ws_sponsors_id,',') from dbo.TBL_WD_SPONSORS where ws_id = 24706
--select * from dbo.TBL_WD_SPONSORS where WS_SPONSORS_ID in(select * from dbo.Comma_Split(ws_sponsors_id,','))
fetch next from splitcursor into #splitc
end
close splitcursor
deallocate splitcursor
And I am getting the Error
Cannot find either column "dbo" or the user-defined function or aggregate "dbo.Comma_Split", or the name is ambiguous.
WS_ID WS_WEBPAGE_ID WS_SPONSORS_ID WS_STATUS WS_CREATEDTS
24706 3 8,7,9,5,5 NULL NULL
But I need the Output
WS_ID WS_WEBPAGE_ID WS_SPONSORS_ID WS_STATUS WS_CREATEDTS
24706 3 8 NULL NULL
24707 3 7 NULL NULL
24708 3 9 NULL NULL
24709 3 5 NULL NULL
24710 3 5 NULL NULL
How to get the Output
Help me out.
Why not re-architect your tables so you only have 1 value entered in your WS_SPONSORS_ID column so it's a foreign-key relationship, then you wouldn't have to do this split function.
It looks like the function you're trying to use does not exist on the database you're running it on. For instance, try this and you'll get the same error:
SELECT dbo.This_Function_Most_Certainly_Doesnt_Exist('Unless you''re trying to break my example.')
This will prove if it is there or not so that you don't have to take my word for it. Run this on the database that you're running your cursor based query on:
SELECT * FROM INFORMATION_SCHEMA.ROUTINES R WHERE R.ROUTINE_NAME = 'Comma_Split'
If you don't get any rows back, that is because the function doesn't exist there. Either you're on the wrong database, the wrong version, you need to run the CREATE FUNCTION on it, or something like that.
EDIT: By the way, as for how to solve the actual problem: Don't use a cursor. A cursor is used as a form of iteration over a data result set. In your cursor you're selecting the value for a single row:
select dbo.Comma_Split(ws_sponsors_id,',') from dbo.TBL_WD_SPONSORS where ws_id = 24706
The way you have it, you'd call that function, on that row, once for every row on the table. Here's a good example of a split function: http://praveenbattula.blogspot.com/2010/06/best-split-udf-function-with-delimeter.html
Using that you would just need to write:
SELECT dbo.fnStringSplitter(ws_sponsors_id, ',') FROM dbo.TBL_WT_SPONSORS WHERE ws_id = 24706