Waveform cursors and "data gates"
I have a waveform graph and want to capture certain periods of the waveform. I want a cursor to appear as a line that is adjustable in length, position and level, much like data gates used in ultrasonic A-Scan applications. I have been doing this in the past by superimposing a picture drawing lines over the graph, but I think data gates using cursors would be easier to manage and simplify things. Does anyone know how to make a cursor appear as a line that can be adjusted for length and position on the graph? And it would be even better if it were adjustable with a touch screen or with the mouse.
Paul
7.1/XP
PaulG.
"I enjoy talking to you. Your mind appeals to me. It resembles my own mind except that you happen to be insane." -- George Orwell
Paul,
There is no one, built-in function in LabVIEW that allows you to do this behavior, but you can easily code it yourself. What you have to do is detect when one of the two cursors is moved and then move the other cursor with it. If you want to also graph a line between the two cursors, you can do that, too, just by plotting it like any other plot.
Coding this behavior is a breeze in LabVIEW 8 because there is now an acutal "Cursor Move" event; I have attached a LabVIEW 8 VI that demonstrates how to implement what you are talking about. In LabVIEW 7.1 and older, this is a little more difficult because there is no "Cursor Move" event, but there is an old post that discusses possible workarounds.
Kind Regards,
E. Sulzer
Applications Engineer
National Instruments
Attachments:
CursorWindow.vi 22 KB
Similar Messages
-
Save multiple waveform graphs and data in one Excel File
Hi there,
I managed to save data from waveform graph into excel file and saving the plot into excel but individually. I'm aiming to save both the data and the plot in an excel file. May I know how do I do it? I've tried using the Write to Measurement File vi and also the invoke node of the waveform chart but it's still not giving me what I've wanted.
The above was only for one analog input channel and I have 2 analog inputs each with their respective sets of data and plot. Is there a way to execute the data and plot of one input in one worksheet and another set of data and plot in another worksheet, all in one excel file?Hi there;
I would recommend using the Report Generation Toolkit; it is the easiest way.
You can use either the Report Express VI or the Appi to save all your data in both formats
Greetings -
Cursor and Update rows based on value/date
SQL Server 2012
Microsoft SQL Server Management Studio
11.0.3128.0
Microsoft Analysis Services Client Tools
11.0.3128.0
Microsoft Data Access Components (MDAC)
6.1.7601.17514
Microsoft MSXML 3.0 4.0 5.0 6.0
Microsoft Internet Explorer
9.11.9600.16518
Microsoft .NET Framework
4.0.30319.18408
Operating System
6.1.7601
The objective of this is to test the Cursor and use it on a production environment after this is fixed. What I would like to do is update rows in a column i duplicated originally called 'HiredDate' from AdventureWorks2012 HumanResources.Employee table. I
made a duplicate column called 'DateToChange' and would like to change it based on a date I have picked, which returns normally 2 results (i.e. date is '04/07/2003'). The code runs but will not change both dates. It did run however with an error but changed
only 1 of the 2 rows because it said ['nothing available in next fetch'].
The code to add the columns and perform the query to get the results I am running this against:
-- ADD column 'DateToChange'
ALTER TABLE [HumanResources].[Employee] ADD DateToChange Date NOT NULL;
-- Copy 'HireDate' data to 'DateToChange'
UPDATE HumanResources.Employee SET DateToChange = HireDate;
-- Change 'DateToChange' to NOT NULL
ALTER TABLE [HumanResources].[Employee] ALTER COLUMN DateToChange Date NOT NULL;
SELECT BusinessEntityID,HireDate, CONVERT( char(10),[DateToChange],101) AS [Formatted Hire Date]
FROM HumanResources.Employee
WHERE [DateToChange] = '04/07/2003';
Code:
USE AdventureWorks2012;
GO
-- Holds output of the CURSOR
DECLARE @EmployeeID INT
DECLARE @HiredDate DATETIME
DECLARE @HiredModified DATETIME
DECLARE @ChangeDateTo DATETIME
--Declare cursor
-- SCROLL CURSOR ALLOWS "for extra options" to pul multiple records: i.e. PRIOR, ABSOLUTE ##, RELATIVE ##
DECLARE TestCursor CURSOR SCROLL FOR
-- SELECT statement of what records going to be used by CURSOR
-- Assign the query to the cursor.
SELECT /*HumanResources.Employee.BusinessEntityID, HumanResources.Employee.HireDate,*/ CONVERT( char(10),[DateToChange],101) AS [Formatted Hire Date]
FROM HumanResources.Employee
WHERE DateToChange = '01/01/1901'
/*ORDER BY HireDate DESC*/ FOR UPDATE OF [DateToChange];
-- Initiate CURSOR and load records
OPEN TestCursor
-- Get first row from query
FETCH NEXT FROM TestCursor
INTO @HiredModified
-- Logic to tell the Cursor while "@@FETCH_STATUS" 0 the cursor has successfully fetched the next record.
WHILE (@@FETCH_STATUS = 0 AND @@CURSOR_ROWS = -1)
BEGIN
FETCH NEXT FROM TestCursor
IF (@HiredModified = '04/07/2003')/*05/18/2006*/
-- Sets @HiredModifiedDate data to use for the change
SELECT @ChangeDateTo = '01/01/1901'
UPDATE HumanResources.Employee
SET [DateToChange] = @ChangeDateTo --'01/01/1901'
FROM HumanResources.Employee
WHERE CURRENT OF TestCursor;
END
-- CLOSE CURSOR
CLOSE TestCursor;
-- Remove any references held by cursor
DEALLOCATE TestCursor;
GO
This query is run successfully but it does not produce the desired results to change the dates
04/07/2003 to 01/01/1901.
I would like the query to essentially be able to run the initial select statement, and then update and iterate through the returned results while replacing the necessary column in each row.
I am also open to changes or a different design all together.
For this query I need:
1. To narrow the initial set of information
2. Check if the information returned, in particular a date, is before [i.e. this current month minus 12 months or
12 months before current month]
3. Next replace the dates with the needed date
[Haven't written this out yet but it will need to be done]
4. After all this is done I will then need to update a column on each row:
if the 'date' is within 12 months to 12 months from the date checked
NOTE: I am new to TSQL and have only been doing this for a few days, but I will understand or read up on what is explained if given enough information. Thank you in advance for anyone who may be able to help.The first thing you need to do is forget about cursors. Those are rarely needed. Instead you need to learn the basics of the tsql language and how to work with data in sets. For starters, your looping logic is incorrect. You open
the cursur and immediately fetch the first row. You enter the loop and the first thing in the loop does what? Fetches another row. That means you have "lost" the values from the first row fetched. You also do not test the success of
that fetch but immediately try to use the fetched value. In addition, your cursor includes the condition "DateToChange = '01/01/1901' " - by extension you only select rows where HireDate is Jan 1 1901. So the value fetched into @HiredModified will
never be anything different - it will always be Jan 1 1901. The IF logic inside your loop will always evaluate to FALSE.
But forget all that. In words, tell us what you are trying to do. It seems that you intend to add a new column to a table - one that is not null (ultimately) and is set to a particular value based on some criteria. Since you intend the
column to be not null, it is simpler to just add the column as not null with a default. Because you are adding the column, the assumption is that you need to set the appropriate value for EVERY row in the table so the actual default value can be anything.
Given the bogosity of the 1/1/1901 value, why not use this as your default and then set the column based on the Hiredate afterwards. Simply follow the alter table statement with an update statement. I don't really understand what your logic
or goal is, but perhaps that will come with a better description. In short:
alter table xxx add DateToChange date default '19010101'
update xxx set DateToChange = HireDate where [some unclear condition]
Lastly, you should consider wrapping everything you do in a transaction so that you recover from any errors. In a production system, you should consider making a backup immediately before you do anything - strongly consider and have a good reason not
to do so if that is your choice (and have a recovery plan just in case). -
How to show two different plots with current system time and date on waveform chart
I am using one waveform chart to display the more than one value continiously. The data are comming properly but i am not getting my system (pc) time and date on x axis. It is showing default date and time (i.e.01/01/1904 5:30:45). Please give me suggestions to display the real time and date on x axis of waveform chart.
How does your data look like? Do you graph waveform data types, dynamic data, or plain arrays/clustes? In the case of plain arrays, you need to set x0 to the absolute start time of your data, e.g. with a property node.
I you would attach your code (or an image) we could offer more specific advice. There are too many possibilities.
LabVIEW Champion . Do more with less code and in less time . -
How to show a waveform signal in data dashboard and UI builder ???
Hey guys.
I have made a program that collect analog data from two vibration sensors and then i use the signalexpress power spectrum to get it in the frequency domain.
I would like to show the signals and the FFT in data dashboard and in UI builder and have some problems doing so.
I'm using Network shared variables and web services.
First question:
It seems that I'm loosing some data when I go through the Network shared double variable. I know that this is becouse i'm using a double variable and I would like to send a waveform but that is not possible becouse the charts in UI builder and data dashboard do not support waveform or?
Second question:
This is almost the same problem as the first question.
Its not possible to show the data directly from the signalexpress power spectrum becouse the XY graph in data dashboard only supports array of cluster of two numerics right ?
Is it possible to convert the data from the signalexpress power spectrum to array of cluster of two numerics ?
Or any other ideas would be appreciatedHi Genex.
It is important to say, that the Network Shared Variables are not lossless. That might explain the behavior you are seeing. I would not expect the update rate of the UI Builder to be that high, which could lead to data loss.
For more information about choose the right network protocol, wether it is only for monitoring or streaming all available data, see the following article:
Using the Right Networking Protocol
Also, I have posted a reply to your other post about your second question.
Best Regards
Alex E. Petersen
Certified LabVIEW Developer (CLD)
Application Engineer
Image House PantoInspect -
I am using Labview 6.1 and my operation system is Windows 98. The date/time properties is configured in the Windows (time and date in the control panel is correct). I generated a waveform using Basic Function Generator.vi and saved this using the Export Waveforms to Spreadsheet File.vi. When I opened this file the date and time was wrong. Only the delta t was correct and it did not show the date neither the time, only a float number. How can I adjust the correct time?
Actually, the date/time is correct, it is expressed as seconds since January 01, 1904.
In order to print the string version of the date/time from your file, you will need to write a utility that converts the date/time to a string or series of strings in whatever format you wish. This can be done by reading the first line (I assume that is the line with the time on it), formatting the date/time into a string, and writing over this information in the file.
The waveform VIs were meant to be used internally to read and write waveform data. Any use beyond that should require you to add formatting and other information/data as necessary. -
Help with if statement in cursor and for loop to get output
I have the following cursor and and want to use if else statement to get the output. The cursor is working fine. What i need help with is how to use and if else statement to only get the folderrsn that have not been updated in the last 30 days. If you look at the talbe below my select statement is showing folderrs 291631 was updated only 4 days ago and folderrsn 322160 was also updated 4 days ago.
I do not want these two to appear in my result set. So i need to use if else so that my result only shows all folderrsn that havenot been updated in the last 30 days.
Here is my cursor:
/*Cursor for Email procedure. It is working Shows userid and the string
You need to update these folders*/
DECLARE
a_user varchar2(200) := null;
v_assigneduser varchar2(20);
v_folderrsn varchar2(200);
v_emailaddress varchar2(60);
v_subject varchar2(200);
Cursor c IS
SELECT assigneduser, vu.emailaddress, f.folderrsn, trunc(f.indate) AS "IN DATE",
MAX (trunc(fpa.attemptdate)) AS "LAST UPDATE",
trunc(sysdate) - MAX (trunc(fpa.attemptdate)) AS "DAYS PAST"
--MAX (TRUNC (fpa.attemptdate)) - TRUNC (f.indate) AS "NUMBER OF DAYS"
FROM folder f, folderprocess fp, validuser vu, folderprocessattempt fpa
WHERE f.foldertype = 'HJ'
AND f.statuscode NOT IN (20, 40)
AND f.folderrsn = fp.folderrsn
AND fp.processrsn = fpa.processrsn
AND vu.userid = fp.assigneduser
AND vu.statuscode = 1
GROUP BY assigneduser, vu.emailaddress, f.folderrsn, f.indate
ORDER BY fp.assigneduser;
BEGIN
FOR c1 IN c LOOP
IF (c1.assigneduser = v_assigneduser) THEN
dbms_output.put_line(' ' || c1.folderrsn);
else
dbms_output.put(c1.assigneduser ||': ' || 'Overdue Folders:You need to update these folders: Folderrsn: '||c1.folderrsn);
END IF;
a_user := c1.assigneduser;
v_assigneduser := c1.assigneduser;
v_folderrsn := c1.folderrsn;
v_emailaddress := c1.emailaddress;
v_subject := 'Subject: Project for';
END LOOP;
END;
The reason I have included the folowing table is that I want you to see the output from the select statement. that way you can help me do the if statement in the above cursor so that the result will look like this:
emailaddress
Subject: 'Project for ' || V_email || 'not updated in the last 30 days'
v_folderrsn
v_folderrsn
etc
[email protected]......
Subject: 'Project for: ' Jim...'not updated in the last 30 days'
284087
292709
[email protected].....
Subject: 'Project for: ' Kim...'not updated in the last 30 days'
185083
190121
190132
190133
190159
190237
284109
286647
294631
322922
[email protected]....
Subject: 'Project for: Joe...'not updated in the last 30 days'
183332
183336
[email protected]......
Subject: 'Project for: Sam...'not updated in the last 30 days'
183876
183877
183879
183880
183881
183882
183883
183884
183886
183887
183888
This table is to shwo you the select statement output. I want to eliminnate the two days that that are less than 30 days since the last update in the last column.
Assigneduser....Email.........Folderrsn...........indate.............maxattemptdate...days past since last update
JIM......... jim@ aol.com.... 284087............. 9/28/2006.......10/5/2006...........690
JIM......... jim@ aol.com.... 292709............. 3/20/2007.......3/28/2007............516
KIM......... kim@ aol.com.... 185083............. 8/31/2004.......2/9/2006............. 928
KIM...........kim@ aol.com.... 190121............. 2/9/2006.........2/9/2006.............928
KIM...........kim@ aol.com.... 190132............. 2/9/2006.........2/9/2006.............928
KIM...........kim@ aol.com.... 190133............. 2/9/2006.........2/9/2006.............928
KIM...........kim@ aol.com.... 190159............. 2/13/2006.......2/14/2006............923
KIM...........kim@ aol.com.... 190237............. 2/23/2006.......2/23/2006............914
KIM...........kim@ aol.com.... 284109............. 9/28/2006.......9/28/2006............697
KIM...........kim@ aol.com.... 286647............. 11/7/2006.......12/5/2006............629
KIM...........kim@ aol.com.... 294631............. 4/2/2007.........3/4/2008.............174
KIM...........kim@ aol.com.... 322922............. 7/29/2008.......7/29/2008............27
JOE...........joe@ aol.com.... 183332............. 1/28/2004.......4/23/2004............1585
JOE...........joe@ aol.com.... 183336............. 1/28/2004.......3/9/2004.............1630
SAM...........sam@ aol.com....183876.............3/5/2004.........3/8/2004.............1631
SAM...........sam@ aol.com....183877.............3/5/2004.........3/8/2004.............1631
SAM...........sam@ aol.com....183879.............3/5/2004.........3/8/2004.............1631
SAM...........sam@ aol.com....183880.............3/5/2004.........3/8/2004.............1631
SAM...........sam@ aol.com....183881.............3/5/2004.........3/8/2004.............1631
SAM...........sam@ aol.com....183882.............3/5/2004.........3/8/2004.............1631
SAM...........sam@ aol.com....183883.............3/5/2004.........3/8/2004.............1631
SAM...........sam@ aol.com....183884.............3/5/2004.........3/8/2004............ 1631
SAM...........sam@ aol.com....183886.............3/5/2004.........3/8/2004............ 1631
SAM...........sam@ aol.com....183887.............3/5/2004.........3/8/2004............ 1631
SAM...........sam@ aol.com....183888.............3/5/2004.........3/8/2004............ 1631
PAT...........pat@ aol.com.....291630.............2/23/2007.......7/8/2008............ 48
PAT...........pat@ aol.com.....313990.............2/27/2008.......7/28/2008............28
NED...........ned@ aol.com.....190681.............4/4/2006........8/10/2006............746
NED...........ned@ aol.com......95467.............6/14/2006.......11/6/2006............658
NED...........ned@ aol.com......286688.............11/8/2006.......10/3/2007............327
NED...........ned@ aol.com.....291631.............2/23/2007.......8/21/2008............4
NED...........ned@ aol.com.....292111.............3/7/2007.........2/26/2008............181
NED...........ned@ aol.com.....292410.............3/15/2007.......7/22/2008............34
NED...........ned@ aol.com.....299410.............6/27/2007.......2/27/2008............180
NED...........ned@ aol.com.....303790.............9/19/2007.......9/19/2007............341
NED...........ned@ aol.com.....304268.............9/24/2007.......3/3/2008............ 175
NED...........ned@ aol.com.....308228.............12/6/2007.......12/6/2007............263
NED...........ned@ aol.com.....316689.............3/19/2008.......3/19/2008............159
NED...........ned@ aol.com.....316789.............3/20/2008.......3/20/2008............158
NED...........ned@ aol.com.....317528.............3/25/2008.......3/25/2008............153
NED...........ned@ aol.com.....321476.............6/4/2008.........6/17/2008............69
NED...........ned@ aol.com.....322160.............7/3/2008.........8/21/2008............4
MOE...........moe@ aol.com.....184169.............4/5/2004.......12/5/2006............629
[email protected]/27/2004.......3/8/2004............1631
How do I incorporate a if else statement in the above cursor so the two days less than 30 days since last update are not returned. I do not want to send email if the project have been updated within the last 30 days.
Edited by: user4653174 on Aug 25, 2008 2:40 PManalytical functions: http://download-west.oracle.com/docs/cd/B10501_01/server.920/a96540/functions2a.htm#81409
CASE
http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96624/02_funds.htm#36899
http://download.oracle.com/docs/cd/B10501_01/appdev.920/a96624/04_struc.htm#5997
Incorporating either of these into your query should assist you in returning the desired results. -
Why use cursor and for loop?
Hi All
So in general why would we use a cursor and a for loop to do update in a stored procedure?
Why wouldnt we just use a single update statement ?
is there compelling reason for using a cursor and a for loop: I am reading some code from a co-worker that the business logic for the select (set need to be updated) is complex but the update logic is simple (just set a flag to (0 or 1 or 2 or 3 or 4).
But eventually the select come down to a key (row_id) so I re-write it using just a single sql statement.
The size of the main table is about 2.6 to 3million rows
Any thoughts on that??
The code below I just do a google for cursor for update example in case for something to play with
-Thanks for all your input
create table f (a number, b varchar2(10));
insert into f values (5,'five');
insert into f values (6,'six');
insert into f values (7,'seven');
insert into f values (8,'eight');
insert into f values (9,'nine');
commit;
create or replace procedure wco as
cursor c_f is
select a,b from f where length(b) = 5 for update;
v_a f.a%type;
v_b f.b%type;
begin
open c_f;
loop
fetch c_f into v_a, v_b;
exit when c_f%notfound;
update f set a=v_a*v_a where current of c_f;
end loop;
close c_f;
end;
exec wco;
select * from f;
drop table f;
drop procedure wco;
Joining multiple tables
create table numbers_en (
id_num number primary key,
txt_num varchar2(10)
insert into numbers_en values (1, 'one' );
insert into numbers_en values (2, 'two' );
insert into numbers_en values (3, 'three');
insert into numbers_en values (4, 'four' );
insert into numbers_en values (5, 'five' );
insert into numbers_en values (6, 'six' );
create table lang (
id_lang char(2) primary key,
txt_lang varchar2(10)
insert into lang values ('de', 'german');
insert into lang values ('fr', 'french');
insert into lang values ('it', 'italian');
create table translations (
id_num references numbers_en,
id_lang references lang,
txt_trans varchar2(10) not null
insert into translations values (1, 'de', 'eins' );
insert into translations values (1, 'fr', 'un' );
insert into translations values (2, 'it', 'duo' );
insert into translations values (3, 'de', 'drei' );
insert into translations values (3, 'it', 'tre' );
insert into translations values (4, 'it', 'quattro');
insert into translations values (6, 'de', 'sechs' );
insert into translations values (6, 'fr', 'six' );
declare
cursor cur is
select id_num,
txt_num,
id_lang,
txt_lang,
txt_trans
from numbers_en join translations using(id_num)
left join lang using(id_lang)
for update of translations.txt_trans;
rec cur%rowtype;
begin
for rec in cur loop
dbms_output.put (
to_char (rec.id_num , '999') || ' - ' ||
rpad (rec.txt_num , 10 ) || ' - ' ||
rpad(nvl(rec.txt_trans, ' '), 10 ) || ' - ' ||
rec.id_lang || ' - ' ||
rpad (rec.txt_lang , 10 )
if mod(rec.id_num,2) = 0 then
update translations set txt_trans = upper(txt_trans)
where current of cur;
dbms_output.put_line(' updated');
else
dbms_output.new_line;
end if;
end loop;
end;
/Edited by: xwo0owx on Apr 25, 2011 11:23 AMAdding my sixpence...
PL/SQL is not that different from a SQL perspective than any other SQL client language like Java or C# or C/C++. PL/SQL simply integrates the 2 languages a heck of a lot better and far more transparent than the others. But make no mistake in that PL/SQL is also a "client" language from a SQL perspective. The (internal) calls PL/SQL make to the SQL engine, are the same (driver) calls made to the SQL engine when using Java and C and the others.
So why a cursor and loops in PL/SQL? For the same reason you have cursors and loops in all these other SQL client languages. There are the occasion that you need to pull data from the SQL engine into the local language to perform some very funky and complex processing that is not possible using the SQL language.
The danger is using client cursor loop processing as the norm - always pulling rows into the client language and crunching it there. This is not very performant. And pretty much impossible to scale. Developers in this case views the SQL language as a mere I/O interface for reading and writing rows. As they would use the standard file I/O read() and write() interface calls.
Nothing could be further from the truth. SQL is a very advance and sophisticated data processing language. And it will always be faster than having to pull rows to a client language and process them there. However, SQL is not Turing complete. It is not the procedural type language that most other languages we use, are. For that reason there are things that we cannot do in SQL. And that should be the only reason for using the client language, like PL/SQL or the others, to perform row crunching using a client cursor loop. -
Hi..
I'm using a ref cursor query to fetch data for a report and works just fine. However i need to use dynamic sql in the query because the columns used in the where condition and for some calculations may change dynamically according to user input from the form that launches the report..
Ideally the query should look like this:
select
a,b,c
from table
where :x = something
and :y = something
and (abs(:x/:y........)
The user should be able to switch between :x and :y
Is there a way to embed dynamic sql in a ref cursor query in Reports 6i?
Reports 6i
Forms 6i
Windows 2000 PROHello Nicola,
You can parameterize your ref cursor by putting the query's select statement in a procedure/function (defined in your report, or in the database), and populating it based on arguments accepted by the procedure.
For example, the following procedure accepts a strongly typed ref cursor and populates it with emp table data based on the value of the 'mydept' input parameter:
Procedure emp_refcursor(emp_data IN OUT emp_rc, mydept number) as
Begin
-- Open emp_data for select all columns from emp where deptno = mydept;
Open emp_data for select * from emp where deptno = mydept;
End;
This procedure/function can then be called from the ref cursor query program unit defined in your report's data model, to return the filled ref cursor to Reports.
Thanks,
The Oracle Reports Team. -
I have a 1-D array of waveform with size = 4 that contain "raw" potentiometer voltage data. I need to manipulate waveform data from index 0 & 1 using the formula shown below to derive a single waveform of angle data. I need to do the same for index 2 & 3 as this is a redundant circuit. I was hoping that the formula node can work on entire arrays and although it can take an array as input, it requires me to index the array in the formula so it becomes a scalar value.
Since the formula is relatively complex, I'd like to keep it in text form but have it automatically work on each point of the two input arrays. This math is done inline with pulling data out of a DAQ and so I need it to be as efficient as possible so that I don't spend too much time on it and potentially overflow the DAQ buffer.
The naive solution would be to wrap the formula nodes with for loops, but I don't know if this is this is an efficient way to do this. I would appreciate any suggestions on how best to tackle this.
Thanks!
Solved!
Go to Solution.Here's how I would do it with no formula nodes or loops requied:
If you want to use the formula node, then you could run a loop inside each formula node while you index through the arrays. The performance difference between formula nodes and the graphical approach should be insignificant. Note that my approach assumes that the array sizes are the same. You could also create a sub VI to contain the math so you don't have to maintain two copies of the same piece of code.
Chris M -
PL/SQL 101 : Cursors and SQL Projection
PL/SQL 101 : Cursors and SQL Projection
This is not a question, it's a forum article, in reponse to the number of questions we get regarding a "dynamic number of columns" or "rows to columns"
There are two integral parts to an SQL Select statement that relate to what data is selected. One is Projection and the other is Selection:-
Selection is the one that we always recognise and use as it forms the WHERE clause of the select statement, and hence selects which rows of data are queried.
The other, SQL Projection is the one that is less understood, and the one that this article will help to explain.
In short, SQL Projection is the collective name for the columns that are Selected and returned from a query.
So what? Big deal eh? Why do we need to know this?
The reason for knowing this is that many people are not aware of when SQL projection comes into play when you issue a select statement. So let's take a basic query...
First create some test data...
create table proj_test as
select 1 as id, 1 as rn, 'Fred' as nm from dual union all
select 1,2,'Bloggs' from dual union all
select 2,1,'Scott' from dual union all
select 2,2,'Smith' from dual union all
select 3,1,'Jim' from dual union all
select 3,2,'Jones' from dual
... and now query that data...
SQL> select * from proj_test;
ID RN NM
1 1 Fred
1 2 Bloggs
2 1 Scott
2 2 Smith
3 1 Jim
3 2 Jones
6 rows selected.
OK, so what is that query actually doing?
To know that we need to consider that all queries are cursors and all cursors are processed in a set manner, roughly speaking...
1. The cursor is opened
2. The query is parsed
3. The query is described to know the projection (what columns are going to be returned, names, datatypes etc.)
4. Bind variables are bound in
5. The query is executed to apply the selection and identify the data to be retrieved
6. A row of data is fetched
7. The data values from the columns within that row are extracted into the known projection
8. Step 6 and 7 are repeated until there is no more data or another condition ceases the fetching
9. The cursor is closed
The purpose of the projection being determined is so that the internal processing of the cursor can allocate memory etc. ready to fetch the data into. We won't get to see that memory allocation happening easily, but we can see the same query being executed in these steps if we do it programatically using the dbms_sql package...
CREATE OR REPLACE PROCEDURE process_cursor (p_query in varchar2) IS
v_sql varchar2(32767) := p_query;
v_cursor number; -- A cursor is a handle (numeric identifier) to the query
col_cnt integer;
v_n_val number; -- numeric type to fetch data into
v_v_val varchar2(20); -- varchar type to fetch data into
v_d_val date; -- date type to fetch data into
rec_tab dbms_sql.desc_tab; -- table structure to hold sql projection info
dummy number;
v_ret number; -- number of rows returned
v_finaltxt varchar2(100);
col_num number;
BEGIN
-- 1. Open the cursor
dbms_output.put_line('1 - Opening Cursor');
v_cursor := dbms_sql.open_cursor;
-- 2. Parse the cursor
dbms_output.put_line('2 - Parsing the query');
dbms_sql.parse(v_cursor, v_sql, dbms_sql.NATIVE);
-- 3. Describe the query
-- Note: The query has been described internally when it was parsed, but we can look at
-- that description...
-- Fetch the description into a structure we can read, returning the count of columns that has been projected
dbms_output.put_line('3 - Describing the query');
dbms_sql.describe_columns(v_cursor, col_cnt, rec_tab);
-- Use that description to define local datatypes into which we want to fetch our values
-- Note: This only defines the types, it doesn't fetch any data and whilst we can also
-- determine the size of the columns we'll just use some fixed sizes for this example
dbms_output.put_line(chr(10)||'3a - SQL Projection:-');
for j in 1..col_cnt
loop
v_finaltxt := 'Column Name: '||rpad(upper(rec_tab(j).col_name),30,' ');
case rec_tab(j).col_type
-- if the type of column is varchar2, bind that to our varchar2 variable
when 1 then
dbms_sql.define_column(v_cursor,j,v_v_val,20);
v_finaltxt := v_finaltxt||' Datatype: Varchar2';
-- if the type of the column is number, bind that to our number variable
when 2 then
dbms_sql.define_column(v_cursor,j,v_n_val);
v_finaltxt := v_finaltxt||' Datatype: Number';
-- if the type of the column is date, bind that to our date variable
when 12 then
dbms_sql.define_column(v_cursor,j,v_d_val);
v_finaltxt := v_finaltxt||' Datatype: Date';
-- ...Other types can be added as necessary...
else
-- All other types we'll assume are varchar2 compatible (implicitly converted)
dbms_sql.DEFINE_COLUMN(v_cursor,j,v_v_val,2000);
v_finaltxt := v_finaltxt||' Datatype: Varchar2 (implicit)';
end case;
dbms_output.put_line(v_finaltxt);
end loop;
-- 4. Bind variables
dbms_output.put_line(chr(10)||'4 - Binding in values');
null; -- we have no values to bind in for our test
-- 5. Execute the query to make it identify the data on the database (Selection)
-- Note: This doesn't fetch any data, it just identifies what data is required.
dbms_output.put_line('5 - Executing the query');
dummy := dbms_sql.execute(v_cursor);
-- 6.,7.,8. Fetch the rows of data...
dbms_output.put_line(chr(10)||'6,7 and 8 Fetching Data:-');
loop
-- 6. Fetch next row of data
v_ret := dbms_sql.fetch_rows(v_cursor);
-- If the fetch returned no row then exit the loop
exit when v_ret = 0;
-- 7. Extract the values from the row
v_finaltxt := null;
-- loop through each of the Projected columns
for j in 1..col_cnt
loop
case rec_tab(j).col_type
-- if it's a varchar2 column
when 1 then
-- read the value into our varchar2 variable
dbms_sql.column_value(v_cursor,j,v_v_val);
v_finaltxt := ltrim(v_finaltxt||','||rpad(v_v_val,20,' '),',');
-- if it's a number column
when 2 then
-- read the value into our number variable
dbms_sql.column_value(v_cursor,j,v_n_val);
v_finaltxt := ltrim(v_finaltxt||','||to_char(v_n_val,'fm999999'),',');
-- if it's a date column
when 12 then
-- read the value into our date variable
dbms_sql.column_value(v_cursor,j,v_d_val);
v_finaltxt := ltrim(v_finaltxt||','||to_char(v_d_val,'DD/MM/YYYY HH24:MI:SS'),',');
else
-- read the value into our varchar2 variable (assumes it can be implicitly converted)
dbms_sql.column_value(v_cursor,j,v_v_val);
v_finaltxt := ltrim(v_finaltxt||',"'||rpad(v_v_val,20,' ')||'"',',');
end case;
end loop;
dbms_output.put_line(v_finaltxt);
-- 8. Loop to fetch next row
end loop;
-- 9. Close the cursor
dbms_output.put_line(chr(10)||'9 - Closing the cursor');
dbms_sql.close_cursor(v_cursor);
END;
SQL> exec process_cursor('select * from proj_test');
1 - Opening Cursor
2 - Parsing the query
3 - Describing the query
3a - SQL Projection:-
Column Name: ID Datatype: Number
Column Name: RN Datatype: Number
Column Name: NM Datatype: Varchar2
4 - Binding in values
5 - Executing the query
6,7 and 8 Fetching Data:-
1 ,1 ,Fred
1 ,2 ,Bloggs
2 ,1 ,Scott
2 ,2 ,Smith
3 ,1 ,Jim
3 ,2 ,Jones
1 ,3 ,Freddy
1 ,4 ,Fud
9 - Closing the cursor
PL/SQL procedure successfully completed.
So, what's really the point in knowing when SQL Projection occurs in a query?
Well, we get many questions asking "How do I convert rows to columns?" (otherwise known as a pivot) or questions like "How can I get the data back from a dynamic query with different columns?"
Let's look at a regular pivot. We would normally do something like...
SQL> select id
2 ,max(decode(rn,1,nm)) as nm_1
3 ,max(decode(rn,2,nm)) as nm_2
4 from proj_test
5 group by id
6 /
ID NM_1 NM_2
1 Fred Bloggs
2 Scott Smith
3 Jim Jones
(or, in 11g, use the new PIVOT statement)
but many of these questioners don't understand it when they say their issue is that, they have an unknown number of rows and don't know how many columns it will have, and they are told that you can't do that in a single SQL statement. e.g.
SQL> insert into proj_test (id, rn, nm) values (1,3,'Freddy');
1 row created.
SQL> select id
2 ,max(decode(rn,1,nm)) as nm_1
3 ,max(decode(rn,2,nm)) as nm_2
4 from proj_test
5 group by id
6 /
ID NM_1 NM_2
1 Fred Bloggs
2 Scott Smith
3 Jim Jones
... it's not giving us this 3rd entry as a new column and we can only get that by writing the expected columns into the query, but then what if more columns are added after that etc.
If we look back at the steps of a cursor we see again that the description and projection of what columns are returned by a query happens before any data is fetched back.
Because of this, it's not possible to have the query return back a number of columns that are based on the data itself, as no data has been fetched at the point the projection is required.
So, what is the answer to getting an unknown number of columns in the output?
1) The most obvious answer is, don't use SQL to try and pivot your data. Pivoting of data is more of a reporting requirement and most reporting tools include the ability to pivot data either as part of the initial report generation or on-the-fly at the users request. The main point about using the reporting tools is that they query the data first and then the pivoting is simply a case of manipulating the display of those results, which can be dynamically determined by the reporting tool based on what data there is.
2) The other answer is to write dynamic SQL. Because you're not going to know the number of columns, this isn't just a simple case of building up a SQL query as a string and passing it to the EXECUTE IMMEDIATE command within PL/SQL, because you won't have a suitable structure to read the results back into as those structures must have a known number of variables for each of the columns at design time, before the data is know. As such, inside PL/SQL code, you would have to use the DBMS_SQL package, just like in the code above that showed the workings of a cursor, as the columns there are referenced by position rather than name, and you have to deal with each column seperately. What you do with each column is up to you... store them in an array/collection, process them as you get them, or whatever. They key thing though with doing this is that, just like the reporting tools, you would need to process the data first to determine what your SQL projection is, before you execute the query to fetch the data in the format you want e.g.
create or replace procedure dyn_pivot is
v_sql varchar2(32767);
-- cursor to find out the maximum number of projected columns required
-- by looking at the data
cursor cur_proj_test is
select distinct rn
from proj_test
order by rn;
begin
v_sql := 'select id';
for i in cur_proj_test
loop
-- dynamically add to the projection for the query
v_sql := v_sql||',max(decode(rn,'||i.rn||',nm)) as nm_'||i.rn;
end loop;
v_sql := v_sql||' from proj_test group by id order by id';
dbms_output.put_line('Dynamic SQL Statement:-'||chr(10)||v_sql||chr(10)||chr(10));
-- call our DBMS_SQL procedure to process the query with it's dynamic projection
process_cursor(v_sql);
end;
SQL> exec dyn_pivot;
Dynamic SQL Statement:-
select id,max(decode(rn,1,nm)) as nm_1,max(decode(rn,2,nm)) as nm_2,max(decode(rn,3,nm)) as nm_3 from proj_test group by id order by id
1 - Opening Cursor
2 - Parsing the query
3 - Describing the query
3a - SQL Projection:-
Column Name: ID Datatype: Number
Column Name: NM_1 Datatype: Varchar2
Column Name: NM_2 Datatype: Varchar2
Column Name: NM_3 Datatype: Varchar2
4 - Binding in values
5 - Executing the query
6,7 and 8 Fetching Data:-
1 ,Fred ,Bloggs ,Freddy
2 ,Scott ,Smith ,
3 ,Jim ,Jones ,
9 - Closing the cursor
PL/SQL procedure successfully completed.
... and if more data is added ...
SQL> insert into proj_test (id, rn, nm) values (1,4,'Fud');
1 row created.
SQL> exec dyn_pivot;
Dynamic SQL Statement:-
select id,max(decode(rn,1,nm)) as nm_1,max(decode(rn,2,nm)) as nm_2,max(decode(rn,3,nm)) as nm_3,max(decode(rn,4,nm)) as nm_4 from proj_test group by id order by id
1 - Opening Cursor
2 - Parsing the query
3 - Describing the query
3a - SQL Projection:-
Column Name: ID Datatype: Number
Column Name: NM_1 Datatype: Varchar2
Column Name: NM_2 Datatype: Varchar2
Column Name: NM_3 Datatype: Varchar2
Column Name: NM_4 Datatype: Varchar2
4 - Binding in values
5 - Executing the query
6,7 and 8 Fetching Data:-
1 ,Fred ,Bloggs ,Freddy ,Fud
2 ,Scott ,Smith , ,
3 ,Jim ,Jones , ,
9 - Closing the cursor
PL/SQL procedure successfully completed.
Of course there are other methods, using dynamically generated scripts etc. (see Re: 4. How do I convert rows to columns?), but the above simply demonstrates that:-
a) having a dynamic projection requires two passes of the data; one to dynamically generate the query and another to actually query the data,
b) it is not a good idea in most cases as it requires code to handle the results dynamically rather than being able to simply query directly into a known structure or variables, and
c) a simple SQL statement cannot have a dynamic projection.
Most importantly, dynamic queries prevent validation of your queries at the time your code is compiled, so the compiler can't check that the column names are correct or the tables names, or that the actual syntax of the generated query is correct. This only happens at run-time, and depending upon the complexity of your dynamic query, some problems may only be experienced under certain conditions. In effect you are writing queries that are harder to validate and could potentially have bugs in them that would are not apparent until they get to a run time environment. Dynamic queries can also introduce the possibility of SQL injection (a potential security risk), especially if a user is supplying a string value into the query from an interface.
To summarise:-
The projection of an SQL statement must be known by the SQL engine before any data is fetched, so don't expect SQL to magically create columns on-the-fly based on the data it's retrieving back; and, if you find yourself thinking of using dynamic SQL to get around it, just take a step back and see if what you are trying to achieve may be better done elsewhere, such as in a reporting tool or the user interface.
Other articles in the PL/SQL 101 series:-
PL/SQL 101 : Understanding Ref Cursors
PL/SQL 101 : Exception Handlingexcellent article. However there is one thing which is slightly erroneous. You don't need a type to be declared in the database to fetch the data, but you do need to declare a type;
here is one of my unit test scripts that does just that.
DECLARE
PN_CARDAPPL_ID NUMBER;
v_Return Cci_Standard.ref_cursor;
type getcardapplattrval_recordtype
Is record
(cardappl_id ci_cardapplattrvalue.cardappl_ID%TYPE,
tag ci_cardapplattrvalue.tag%TYPE,
value ci_cardapplattrvalue.value%TYPE
getcardapplattrvalue_record getcardapplattrval_recordtype;
BEGIN
PN_CARDAPPL_ID := 1; --value must be supplied
v_Return := CCI_GETCUSTCARD.GETCARDAPPLATTRVALUE(
PN_CARDAPPL_ID => PN_CARDAPPL_ID
loop
fetch v_return
into getcardapplattrvalue_record;
dbms_output.put_line('Cardappl_id=>'||getcardapplattrvalue_record.cardappl_id);
dbms_output.put_line('Tag =>'||getcardapplattrvalue_record.tag);
dbms_output.put_line('Value =>'||getcardapplattrvalue_record.value);
exit when v_Return%NOTFOUND;
end loop;
END; -
How to get rid of extra data in character buffers for key and data
Hi -
Using the C API, I am using a cursor to just pull all the key/value pairs from a database.
Here is the relevant code:
/* Initialize key/data structures. */
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.flags = DB_DBT_MALLOC;
data.flags = DB_DBT_MALLOC;
if((ret = dbp->cursor(dbp, NULL, &cursor, 0)) != 0) {
dbp->err(dbp, ret, "%s", DATABASE);
goto err;
while((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
fprintf(cgiOut, "%s|||%s\n", (char *)key.data, (char *)data.data);
free(key.data);
free(data.data);
if(ret != DB_NOTFOUND) {
dbp->err(dbp, ret, "cursor->c_get");
goto err;
This produces lines that in many cases contain extra characters at the ends, as though the malloced data was not being cleared before use. I wrote this routine originally in Java, and solved the problem there with the "setReuseBuffer(false)" Database Entry routine. Is there something similar in C?
Garey MillsSandra -
I am using 4.5. I have solved the problem, however, by using the following code
while((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
char keyd, datad;
keyd = (char *)key.data;
datad = (char*)data.data;
keyd[key.size] = '\0';
datad[data.size] = '\0';
So I am just using the size data returned to null terminate the strings in the buffers. Now everything works. Thanks for your response;
Garey Mills -
How to queue two waveforms in dynamic data
I am collecting two waveforms for voltage data via DAQ assistant. The output is dynamic data which I can view as two waveforms via a chart.
I am sending the retrived data to a queue for storage until a seperate loop can flush the queue and write the data to a measurement file via the Write to Measurement File vi.
When I retrive my measurement file only one of the two waveforms have been stored.
I have troubleshot the issue and have determined the correct data is going into the queueI'd recommend posting some code. Are you passing in the two waveforms to the queue as an array of waveforms, or a cluster of waveforms? Without seeing the code, it is just a guess as to what is going on.
-
Custom Cursor and Drag Image in 1.4 DnD
What is the proper way to provide a custom cursor and drag image in 1.4 DnD? Say we initiate the drag on a component that supports data transfer (such as a JTree).
Well, this is strange. My app was locking up (100% CPU) when using DND from windows Explorer.
I noticed that another part of the same appliction was working perfictly. The difference: JFrame .vs. JDialog. I switched the offending JDialog to a JFrame and all works perfictly now!
java version "1.4.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-b92)
Java HotSpot(TM) Client VM (build 1.4.0-b92, mixed mode)
I would like to switch back at some point, but my g.setXORMode() problem is a much bigger issue for us. (That is, once you enter the XOR mode, you can not go back!) -
Hello.
I need help creating a cursor and loop that will update a column based on data from another column. I keep getting the following errors. Any suggestions? These identifiers are valid tables in columns in my data base.
SQL> DECLARE
2
3 CURSOR c1 IS
4 SELECT SectionID, Coursename, StudentID, FinalGrade from Registration, Section, Grade WHERE registration.sectionid = grade.sectionid and registrationid.studentid = grade.studentid;
5
6 v_SectionID number(10);
7 v_CourseName varchar(20);
8 v_StudentID number(10);
9 v_FinalGrade varchar2(5);
10
11 BEGIN
12
13 OPEN c1;
14
15 LOOP
16 FETCH c1
17 INTO v_SectionID, v_CourseName, v_StudentID, v_FinalGrade;
18 EXIT WHEN c1%NOTFOUND;
19
20 update grade
21
22 SET FinalGrade = translate(substr(v_CourseName,-1,1),'ABCDEFGHIJKLMNOPQRSTUVWXYZ','AAAAAABBBBBCCCCCDDDDEEEEEE')
23
24 where Grade.StudentID = Registration.StudentID;
25
26 END LOOP;
27 CLOSE c1;
28 END;
29 /
SELECT SectionID, Coursename, StudentID, FinalGrade from Registration, Section, Grade WHERE registration.sectionid = grade.sectionid and registrationid.studentid = grade.studentid;
ERROR at line 4:
ORA-06550: line 4, column 140:
PL/SQL: ORA-00904: "REGISTRATIONID"."STUDENTID": invalid identifier
ORA-06550: line 4, column 3:
PL/SQL: SQL Statement ignored
ORA-06550: line 24, column 27:
PL/SQL: ORA-00904: "REGISTRATION"."STUDENTID": invalid identifier
ORA-06550: line 20, column 3:
PL/SQL: SQL Statement ignored863737 wrote:
I am taking a intro course online so I am trying to teach myself this. The most basic rule for designing and writing good Oracle code that will perform and will scale is to use SQL for what it is good at and using PL/SQL for what it is good at. And between the two, SQL is by far superior when it comes to crunching data in the database.
The simple maxim is Maximize SQL. Minimize PL/SQL.
What you want to do with that PL/SQL code can be done using SQL only. And since you are learning SQL and PL/SQL, it is very important that you learn the correct way to do this.
Have a look at Oracle® Database SQL Language Reference guide.
The update syntax supports:
UPDATE <dml_expression_clause>
Where this +<dml_expression_clause>+ is a SELECT statement (aka an in-line view) - that can contain joins.
Simple example. You join tables t1 and t2 to identify the rows in t1 to update. The +<dml_expression_clause>+ will define this join. The actual update part (using the SET clause) will update the relevant rows in t1.
This is also called an updatable view - though the +<dml_expression_clause>+ does not need to use an actual view.
Have a look at AskTom for a discussion on this. Also note that you can also create a view and define an "instead-of" trigger on the view - this particular feature enhances the ability to update DML expressions - but is not a prerequisite.
Note that there can be very significant performance degradation by NOT using an UPDATE on a +<dml_expression_clause>+ and instead coding that manually via PL/SQL.
So do yourself a favour and learn the CORRECT way to resolve the type of UPDATE problem you have.
Maybe you are looking for
-
Trouble with Photoshop CS5.1 eps images with clipping path placed in FHMX
Photoshop CS5 eps files, clearcut with clipping paths now show a black box instead of a transparent background when placing in Freehand MX. Tiff files with clipping paths work OK but look ugly and make Freehand redraw the screen all the time, thus sl
-
How to update characteristics of DMS document
Hello, I have a requirement to update characteristics right after document creation. I use BAPI_DOCUMENT_CREATE2 to create the document: CALL FUNCTION 'BAPI_DOCUMENT_CREATE2' EXPORTING documentdata = ls_doc IMPORTING docume
-
Update to Prime LMS 4.2.3
Hello After update to LMS 4.2.3 is get always a message to confirm when login: "Server is capable for performance tuning. Please enable PTT ...." Even when i setup PTT the message will come again next time i login. The message comes also s
-
Can someone explain to me how to validate a XML document against a schema using JDOM please ? Thank's a lot
-
I was doing a software update and in the middle of it got me an error. try to restore could not do it either and went blank no software in my ipad to be able to boot it up