Auto increment with XQuery Update? - xquery

Does XQuery Update support auto increment attributes, just like auto increment fields in SQL?
I'm using BaseX as my database.

Given an answer from Christian GrĂ¼n on the BaseX mailing list, this is doable when the node one is adding is defined in the XQuery Update statement, and hence can be enhanced using an {enclosed expression} before inserting it:
You might specify the attribute counter within your XML file/database
and increment it every time when you insert an element. A simple
example:
input.xml:
<root count="0"/>
insert.xq:
let $root := doc('input.xml')/root
let $count := $root/#count
return (
insert node <node id='{ $count }'/> into $root,
replace value of node $count with $count + 1
)
I've not been able to achieve the same with an external org.w3c.dom.Document created in Java, and added to the XML database using XQJ and declare variable $doc external. Here, one might be tempted to update the "auto-increment" data after adding the document. However, the processing model defines that changes are not visible until all the commands have been queued (the Pending Update List). Hence a new document or node is, by definition, simply not visible for updates in the same FLWOR expression. So:
db:add('db1', '<counters comment-id="0"/>', 'counters')
...followed by repetitive executions of the following, will NOT work:
let $doc := document{ <note id=""><text>Hello world</text></note> }
let $count := /counters/#comment-id
return (
db:add('db1', $doc, 'dummy'),
replace value of node $count with $count + 1
for $note in /note[#id='']
return replace value of node $note/#id with $count (: WRONG! :)
)
Above, the last inserted document will always have <note id="">, and will not be updated until the next document is added. (Also, it would not work when somehow multiple documents with <note id=""> would exist.)
Though in the example above one could successfully delete the for $note in ... part and use:
let $doc := document{ <note id="{ $count }"><text>Hello world</text></note> }
...I had no luck setting <note id="{ $count }"> in the Document in the Java code, as that enclosed expression would not be replaced then.
Finally, some state for similar solutions:
[...] perform badly as it will lock out concurrent updates. You should consider using xdmp:random() to generate a 64 bit random number for your unique identifier.
In our case, the id would also be used in URLs; not too nice then.
See also XRX/Autoincrement File ID and Using XQuery to return Document IDs.

This would depend on the implementation of the underlying data-store, because the auto-increment attribute is on the column definition in relational databases.
Probably, "yes".

Related

How can I create a new SQLite file and table at runtime using FieldDefs?

I'm using Delphi Seattle to create a brand new table in a brand new SQLite file and using only FieldDefs and non-visual code. I can create a table using the ExecSQL ('CREATE TABLE....' ) syntax but not as shown below (I get 'No such table 'MyTable' which is raised when I execute the CreateDataSet call). I'd like some solution that allows me to work with FieldDefs. This code is modelled on the example here. I notice though, that there is note regarding CreateDataSet that it only applies to TFDMemTable. Is there a runtime way of creating an SQLite table without using ExecSQL?
procedure Test;
const
MyDBFile = 'c:\scratch\hope.db';
var
Connection : TFDConnection;
DriverLink : TFDPhysSQLiteDriverLink;
Table : TFDTable;
begin
DeleteFile( MyDBFile );
DriverLink := TFDPhysSQLiteDriverLink.Create( nil );
Connection := TFDConnection.Create( nil );
try
Connection.Params.Values['DriverID'] := 'SQLite';
Connection.Params.Values['Database'] := MyDBFile;
Connection.Connected := True;
Table := TFDTable.Create( nil );
try
Table.TableName := 'MyTable';
Table.Connection := Connection;
Table.FieldDefs.Add( 'one', ftString, 20 );
Table.FieldDefs.Add( 'two', ftString, 20 );
Table.CreateDataSet;
// I would add records here....
finally
Table.Free;
end;
finally
Connection.Free;
DriverLink.Free;
end;
end;
CreateDataSet is usually a local operation for initializing a client-side dataset into an empty state. If TClientDataSet is anything to go by, afaik it cannot be used create a server-side table.
To create an actual server table, I would expect to have to construct the DDL SQL to create the table and then execute it using ExecSQL on the (client-side) dataset, as you have already tried.
update
The following seems to satisfy your requirement to do everything in code, though using a TFDTable component, which doesn't surface FieldDefs, so I've used code-created TFields instead. Tested in D10 Seattle.
procedure TForm3.CreateDatabaseAndTable;
const
DBName = 'd:\delphi\code\sqlite\atest.sqlite';
var
AField : TField;
begin
if FileExists(DBName) then
DeleteFile(DBName);
AField := TLargeIntField.Create(Self);
AField.Name := 'IDField';
AField.FieldName := 'ID';
AField.DataSet := FDTable1;
AField := TWideStringField.Create(Self);
AField.Size := 80;
AField.Name := 'NameField';
AField.FieldName := 'Name';
AField.DataSet := FDTable1;
FDConnection1.Params.Values['database'] := DBName;
FDConnection1.Connected:= True;
FDTable1.TableName := 'MyTable';
FDTable1.CreateTable(False, [tpTable]);
FDTable1.Open();
FDTable1.InsertRecord([1, 'First']);
FDConnection1.Commit;
FDConnection1.Connected:= False;
end;
I expect that someone a bit more familiar than I am could do similar using a TFDMemTable's FieldDefs if it were correctly connected to a server-side component (FDCommand?) via an FDTableAdaptor.
Fwiw, I've used a LargeInt ID field and WideString Name field because trying to use Sqlite with D7 a while back, I had no end of trouble trying to use Integer and string fields.
Btw, you if you know the structure you require in advance of deployment, you might find that you get more predictable/robust results if you simply copy an empty database + table into place, rather than try and create the table in situ. Ymmv, of course.
I would NEVER dream of creating database tables using fielddefs because you wind up having tables without a proper primary key, indexes and referential integrity. The resulting tables are totally "dumbed down".
Whenever you have a "where" clause in a query the database would do a full table scan to find the records matching the query. So your database slows down (and CPU use increases) with size. That's just bad design.
Regards,
Arthur
You can use the app SQLite Expert Professional, create SQLite database.
And using FDConnection connect to the database. And use it.
Method to database SQLite, the same way that MartynA have said.
Begin
FDConnection1.Connected:=false;
FDConnection1.Params.Clear;
FDConnection1.Params.Database:='D:\SQLiteDatabase.db';
FDConnection1.ConnectionDefName:='SQLite_Demo';
FDConnection1.DriverName:='SQLite';
FDConnection1.Connected:=true;
FDTable1.Open;
End;

How to delete a row that has the same ID as a variable in an SQLite database

I've got a function that I'd like use to delete a row in my database. This is the only way I've used the DELETE statement to remove a row before but I want the 1 to be replaced by a variable called recID so that the value of recID is the row ID number which is deleted. So if recID = 6, I want the function to delete the row with ID = 6. I hope that makes sense.
'DELETE FROM MyRecords WHERE ID=1';
The notation I've been using is the following, if it helps or makes any difference.
db.transaction(function(transaction) {
transaction.executeSql( //DELETE STATEMENT HERE );
});
executeSql supports arguments (check definition).
Use it like:
db.transaction(function(transaction) {
transaction.executeSql("DELETE FROM MyRecords WHERE ID=?", [recId]);
});
If you're certain that your variable, recID, will only ever contain numbers, you can just use:
transaction.executeSql("DELETE FROM MyRecords WHERE ID=" + recID);
If recID comes from outside your application (user input) however, it either needs to be sanitized, or use a prepared statement and use the database API to set the parameter after the statement has been prepared. Otherwise you open yourself up to SQL injection attacks.
I don't know the details of your SQLite wrapper, or what version of SQLite it wraps, but creating a prepared statement using the SQLite3 C API would go something like this:
// sqlite3* db = ...
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, "DELETE FROM MyRecords WHERE ID=?", -1, &stmt, 0);
sqlite3_bind_int(stmt, 1, recID);
sqlite3_step();
// ...
sqlite3_finalize(stmt);
This simple example excludes all the error checking you'd want to do in a real application, but since you're using a wrapper that has different syntax anyway, you'd have to figure out how it wraps these functions anyway.

How to retrieve a value of an attribute from the XML stored across multiple rows using Oracle-Xquery?

We are using Oracle 11g database with XMLDB installation. We are having table with XMLType columns. The structure of the XML will be same for all the rows in a table. The table will have other fiedls also.
Now I want to retrieve only the values of the particular node's attribute values from all the rows as a string with some other relational fields. The table columns retrieved can be like TemplateId, TemplateVid,TemplatepartId.
The structure of the XML can be as follows:
<Template ID=1000 VID=1>
<TemplateParts>
<Template ID="4000" VID="1"/>
<Template ID="4001" VID="1"/>
</TemplateParts>
</Template>
So the table will have data for Template with TemplateId,Vid and TemplateXML. The TemplateXML field is an XMLType field. Now I want to retrieve all the TemplateId,Vid and its refereced template partIds as an XML table. The output should be as follows:
TemplateId - TemplateVid - TemplatePartId - TemplatepartVid
1000 1 4000 1
1000 1 4001 1
So anybody comes up with a correct Xquery for the above requirement.
Your requirement is not clear but to start you off and hopefully get you some additional comment from the wider XQuery community on StackOverflow here is a quick example. Hope this helps :)
xquery version "1.0";
<html>
<head>
<title>Sample</title>
</head>
<body>
<div align="center">
<table>
<tr><th>TemplateId</th><th>TemplateVid</th><th>TemplatePartId</th><th>TemplatepartVid</th></tr>
{
let $sample as element()* := (<root><Template ID="1000" VID="1"><TemplateParts><Template ID="4000" VID="1" /><Template ID="4001" VID="1" /></TemplateParts></Template></root>)
for $e in $sample/Template
return
for $tp in $e/TemplateParts/Template
return
(<tr><td>{data($e/#ID)}</td><td>{data($e/#VID)}</td><td>{data($tp/#ID)}</td><td>{data($tp/#VID)}</td></tr>)
}
</table>
</div>
</body>
</html>
As I have mentioned in my earlier comment, I have managed to get the IDs and VIDs which is stored under the Node /Template/TemplateParts/Template from xmltype column of all the rows. The query is as follows:
select distinct x.value as TemplatePartId,Y.Value as Vid from
TempVersion t ,
xmltable('/Template/TemplateParts/Template' passing t.CONTENT columns value varchar2(10) path '#ID' ) x,
xmltable('/Template/TemplateParts/Template' passing t.CONTENT columns value varchar2(10) path '#VID' ) y
order by TemplatePartId;
If sombody know better format,please post your sample query. I need as a normal query as the above format is not supported by my ORM tool. If you look at the above query you can notice that the XMLTable expression is to be placed after the From clause. This gives trouble when I try to form this query through my LLBLGen ORM tool.

How to dynamically add content to email in PL/SQL?

I'm using Oracle apex and am trying to practice with automatic emails. Ideally, this is how the scenario would go: A user can 'recommend' games to friends through his wishlist. The user selects the game(s) they would like to recommend though a checkbox, then selects the friends and email content. The code I have in place for this is as follows:
DECLARE
content VARCHAR2(4000) := :P4_EMAIL || 'Here are the game(s):';
game VARCHAR2(100);
i NUMBER := 1;
/*Declare the cursor and it's query */
cursor CURSOR IS
SELECT name
from gs_games
where game_id = APEX_APPLICATION.G_F01(i)
FOR UPDATE;
BEGIN
FOR i in 1..APEX_APPLICATION.G_F01.count
LOOP
OPEN cursor;
FETCH cursor INTO game;
CLOSE cursor;
END LOOP;
htmldb_mail.Send(p_to => :P4_FRIENDS,
p_from => 'gametracker#gametracker.com',
p_subj => 'Game recommendations from ' || :F56_USER_NAME,
p_body => content || ' ' || game);
END;
This only partially works. For a single selection, everyone works perfectly, but once the user selects multiple games, then the email only contains the name of the first game checked off, instead of every game as it should. I realize this is tied to the way I've got my cursor set up, but I'm not entirely sure how I can use it while keeping that for loop active. Does anyone have any ideas? Thank you.
The immediate problem is that every time you fetch the next row from the cursor into your local variable game, you are overwriting the prior value of that variable. Once you've fetched every row from the cursor, game will have the value of whatever the last row you processed was.
Assuming that you want your email to contain a comma-separated list of game names, you could do something like
-- You probably want to create this function inside of a package that provides other methods
-- for interacting with games
CREATE OR REPLACE FUNCTION get_game_name( p_game_id IN gs_games.game_id%type )
RETURN gs_games.name%type
IS
l_name gs_games.name%type;
BEGIN
SELECT name
INTO l_name
FROM gs_games
WHERE game_id = p_game_id;
RETURN l_name;
END get_game_name;
DECLARE
l_content VARCHAR2(4000) := :P4_EMAIL || 'Here are the game(s):';
l_game_list VARCHAR2(100);
BEGIN
FOR i in 1..APEX_APPLICATION.G_F01.count
LOOP
l_game_list := l_game_list || ', ' || get_game_name( APEX_APPLICATION.G_F01(i) );
END LOOP;
l_game_list := LTRIM( ', ' );
apex_mail.send( p_to => :P4_FRIENDS,
p_from => 'gametracker#gametracker.com',
p_subj => 'Game recommendations from ' || :F56_USER_NAME,
p_body => l_content || ' ' || l_game_list);
END;
A few notes about style
Naming a cursor CURSOR is problematic and should be avoided at all costs. If you do need to declare a cursor, you really ought to give it a meaningful name.
Local variables generally ought to be named in such a way that differentiates them from the names of columns in tables (in my case, I use a l_ prefix for local variables and a p_ prefix for parameters though there are many different valid conventions. This is important in PL/SQL because identifiers in SQL statements are resolved first using the names of columns in the table and then using local variables. That makes it far too easy to inadvertently write code that uses a column when you meant for it to use a local variable if you don't have some convention to make it clear which you're using.
You don't want to use a cursor to select a single row of data. If you have a query that you know should return exactly 1 row, use a SELECT INTO.
I created a separate function to get the name for a particular game_id because that maximizes reuse-- there will likely be many other places that you need to do something similar so you want to write that code once and use it many times. It would be perfectly legal to put the SELECT INTO into the loop in your anonymous PL/SQL block, it would be preferable to factor that code out into a function.
Assuming you are using a vaguely recent version of APEX, you should be using the apex_mail package not the old htmldb_mail package. There shouldn't be any functional difference between the two but HTML DB was the old name for Oracle APEX many releases ago so it's a good idea to phase out the use of the old package names.

Using prepared statements and full-text-search in SQLite

I'm using the SQLite C interface to write an application. Since I like security, I'm using prepared statements to query the database. In one such query, I'm selecting rows from a virtual database using the MATCH keyword for full-text-searching. Here's an example:
SELECT * FROM Emails
WHERE ( Subject LIKE ?001 OR ?001 IS NULL )
AND ( Author LIKE ?002 OR ?002 IS NULL )
AND ( Body MATCH ?003 OR ?003 IS NULL )
This allows the user to enter any terms (Subject, Author, or Body) individually or in any combination to do a search. Any term that isn't entered, I'll bind NULL to that parameter. The problem with that statement is that you can't use the OR keyword with the MATCH keyword. I'm looking for a statement I can use with the MATCH keyword to return all rows if not searching in the Body column. Is there such a statement?
I suggest the following:
SELECT * FROM emails
WHERE ...
AND ( CASE (SELECT COUNT(*) FROM emails WHERE body MATCH ?003)
WHEN 0 THEN 1
ELSE body MATCH ?003
END )
I ended up modifying the SQL statement at runtime to replace MATCH with LIKE '%'. Not very elegant, but it works for now.

Resources