I am using Delphi2010 and I'm trying to do an insert into multiple tables. I don't know the best way to do this. What I'm wondering is if there is a possible way to do one insert using one of Delphi's tools like the TQuery or TClientDataSet or would it be better to use code (we use Pascal language). An array maybe? I haven't been using Delphi that long but I have inserted and updated info into one table before, not multiple. Also, these tables use pretty much the same field names.
Any help would be greatly appreciated.
Thanks in advance!!
Call a stored procedure to update your tables simultaneously, with a transaction wrapper. Or re-design your database to eliminate duplicate/redundant data, so that you would never need to update several tables at once.
Note that this answer is perfectly valid, given the information provided in the question...
(Note: it's late, couldn't sleep, bored. This is what you get, given the quality of the information in the question!)
Another possible solution could be to make a updateable view on the database, and update the view from Delphi.
Making a updateable view just moves the work of updating 2 tables to the SQL instead of in Delphi.
This moves the business logic to the sql instead of in the Delphi. It propobly also generates less network trafic.
As Chris writes: use transactions, when 2 or more updates/inserts is dependent of each other.
Which data access componentes do you use ?
Which restrictions do you have ?
do you want to insert the same values into both tables ?
why not easy like:
for i = low(tables) to high(tables) do
begin
query.sql.text := 'insert into '+tables[i]+' (fields) values('+ ...)';
query.execsql;
end;
Related
I'm usign the dbExpress components within the Embarcadero C++Builder XE environment.
I have a relatively large table with something between 20k and 100k records, which I display in a DBGrid.
I am using a DataSetProvider, which is connected to a SQLQuery and a ClientDataSet, which is connected to the DataSetProvider.
I also need to analyze the data and therefore I need to run through the whole table. For smaller tables I always used code, which is basically something like this:
Form1->ClientDataSet1->First();
while(!Form1->ClientDataSet1->Eof){
temp=Form1->ClientDataSet1->FieldByName("FailReason")->AsLargeInt;
//do something with temp
Form1->ClientDataSet1->Next();
}
Of course this works out, but it is very slow, when I need to run through the whole DBGrid. For some 50000 records in can take up to some minutes. My suspicion is that the most perform is lost since the DBGrid needs to be repainted as the actual Dataset increments its address.
Therefore I am looking for a method which allows me to either read the data without manipulating the actual ClientDataSet. Maybe a method which copies the data of a column into a variable, or another way to run through the datasets, which is more efficient. I am sure if I would have a copy in a variable the operation would take less than a few seconds...
I googled now for hours, but didn't find anything useful so far.
Best regards,
Bodo
if your cds is connected to some db-aware control(-s) (via TDataSource) then first of all consider using DisableControls()
another option would be to avoid utilizing FieldByName within the loop
I didn't come across in any such scenario where we have to use stored procedure instead of Calculation View, but I read many sites where it is mentioned. One can use Stored Procedure in complex scenarios, but I am confused which scenarios are meant.
Can anyone suggest me such scenarios where we have to use Stored Procedure instead of Graphical Calculation View?
Hierarchies
If you are looking for the parents (or children) of an object for undetermined depth, you have to do many SELECTs in a loop.
If you use views, the loop has to be on ABAP side, causing many roundtrips between the application server and the DB.
Stored procedures are very beneficial in this case, as they can run the loops on HANA side. You only have to more the end result through the network.
Sidenote: you should be using CDS views instead of Calculation views, as they offer many benefits.
First of all they are used by SAP internally in S/4 products, making CDS the way of the present and future.
Also they are ABAP objects, transported together with the referencing ABAP coding.
In a stored procedure, or in an AMDP you can use a script code block which can contain more than a single SELECT statement. You can store temp tables storing outcomes of previous SELECT commands in that AMDP and use later for example.
AMDP enables developers to keep the business logic in it.
But if you are using a view, you are generally limited with allowed functions with a single SELECT statement
For example, I could not use TRIM function within a CDS view but can use in AMDP
I need some advice please, I have been looking for help on this topic but it's not something you find so often. I am also quite new to Zend, so please excuse my terminology
I have a few large sql queries coming up. Most of my other queries are quite small, just a couple of joins etc, but these ones consist of many queries (drop and create temporary tables which together form the final select.) For example
DROP table if exists tmp_abc;
CREATE temporary table tmp_abc as SELECT .... From ... Group By //finish statement
Consider 20 other of these, then a final select which pulls a lot of data from one table.
Can anyone offer some advice on the best solution to tackle this problem?
Would this be possible using some RAW sql adapter or? ... I am kinda of tempted to sod the MVC principle for this based on the complexity/size of the query, but it is something I would like to know for the future which action I should go.
One possibility is to do most of it on database level, some stored procedure(s)/maybe view or two if needed. And then select from that.
I am scanning an SQLite database looking for all matches and using
OneFound:=False;
if tbl1.FieldByName('Name').AsString = 'jones' then
begin
OneFound:=True;
tbl1.Next;
end;
if OneFound then // Do something
or should I be using
if not(OneFound) then OneFound:=True;
Is it faster to just assign "True" to OneFound no matter how many times it is assigned or should I do the comparison and only change OneFuond the first time?
I know a better way would be to use FTS3, but for now I have to scan the database and the question is more on the approach to setting OneFound as many times as a match is encountered or using the compare-approach and setting it just once.
Thanks
Your question is, which is faster:
if not(OneFound) then OneFound:=True;
or
OneFound := True;
The answer is probably that the second is faster. Conditional statements involve branches which risks branch mis-prediction.
However, that line of code is trivial compared to what is around it. Running across a database one row at a time is going to be outrageously expensive. I bet that you will not be able to measure the difference between the two options because the handling of that little Boolean is simply swamped by the rest of the code. In which case choose the more readable and simpler version.
But if you care about the performance of this code you should be asking the database to do the work, as you yourself state. Write a query to perform the work.
It would be better to change your SQL statement so that the work is done in the database. If you want to know whether there is a tuple which contains the value 'jones' in the field 'name', then a quicker query would be
with tquery.create (nil) do
begin
sql.add ('select name from tbl1 where name = :p1 limit 1');
sql.params[0].asstring:= 'jones';
open;
onefound:= not isempty;
close;
free
end;
Your syntax may vary regarding the 'limit' clause but the idea is to return only one tuple from the database which matches the 'where' statement - it doesn't matter which one.
I used a parameter to avoid problems delimiting the value.
1. Search one field
If you want to search one particular field content, using an INDEX and a SELECT will be the fastest.
SELECT * FROM MYTABLE WHERE NAME='Jones';
Do not forget to create an INDEX on the column, first!
2. Fast reading
But if you want to search within a field, or within several fields, you may have to read and check the whole content. In this case, what will be slow will be calling FieldByName() for each data row: you should better use a local TField variable.
Or forget about TDataSet, and switch to direct access to SQLite3. In fact, using DB.pas and TDataSet requires a lot of data marshalling, so is slower than a direct access.
See e.g. DiSQLite3 or our DB classes, which are very fast, but a bit of higher level. Or you can use our ORM on top of those classes. Our classes are able to read more than 500,000 rows per second from a SQLite3 database, including JSON marshalling into objects fields.
3. FTS3/FTS4
But, as you guessed, the fastest would be indeed to use the FTS3/FTS4 feature of SQlite3.
You can think of FTS4/FTS4 as a "meta-index" or a "full-text index" on supplied blob of text. Just like google is able to find a word in millions of web pages: it does not use a regular database, but full-text indexing.
In short, you create a virtual FTS3/FTS4 table in your database, then you insert in this table the whole text of your main records in the FTS TEXT field, forcing the ID field to be the one of the original data row.
Then, you will query for some words on your FTS3/FTS4 table, which will give you the matching IDs, much faster than a regular scan.
Note that our ORM has dedicated TSQLRecordFTS3 / TSQLRecordFTS4 kind of classes for direct FTS process.
Let's say I have 'myStoredProcedure' that takes in an Id as a parameter, and returns a table of information.
Is it possible to write a SQL statement similar to this?
SELECT
MyColumn
FROM
Table-ify('myStoredProcedure ' + #MyId) AS [MyTable]
I get the feeling that it's not, but it would be very beneficial in a scenario I have with legacy code & linked server tables
Thanks!
You can use a table value function in this way.
Here is a few tricks...
No it is not - at least not in any official or documented way - unless you change your stored procedure to a TVF.
But however there are ways (read) hacks to do it. All of them basically involved a linked server and using OpenQuery - for example seehere. Do however note that it is quite fragile as you need to hardcode the name of the server - so it can be problematic if you have multiple sql server instances with different name.
Here is a pretty good summary of the ways of sharing data between stored procedures http://www.sommarskog.se/share_data.html.
Basically it depends what you want to do. The most common ways are creating the temporary table prior to calling the stored procedure and having it fill it, or having one permanent table that the stored procedure dumps the data into which also contains the process id.
Table Valued functions have been mentioned, but there are a number of restrictions when you create a function as opposed to a stored procedure, so they may or may not be right for you. The link provides a good guide to what is available.
SQL Server 2005 and SQL Server 2008 change the options a bit. SQL Server 2005+ make working with XML much easier. So XML can be passed as an output variable and pretty easily "shredded" into a table using the XML functions nodes and value. I believe SQL 2008 allows table variables to be passed into stored procedures (although read only). Since you cited SQL 2000 the 2005+ enhancements don't apply to you, but I mentioned them for completeness.
Most likely you'll go with a table valued function, or creating the temporary table prior to calling the stored procedure and then having it populate that.
While working on the project, I used the following to insert the results of xp_readerrorlog (afaik, returns a table) into a temporary table created ahead of time.
INSERT INTO [tempdb].[dbo].[ErrorLogsTMP]
EXEC master.dbo.xp_readerrorlog
From the temporary table, select the columns you want.