Delphi update a joined query - sql

The application is datasnap (with sqlite database). I run the following query against two tables (LOKACIJE and UPORABNIKI):
procedure TForm4.FormShow(Sender: TObject);
begin
ClientDataSet1.Close;
ClientDataSet1.CommandText :='';
ClientDataSet1.CommandText := 'select lokacije.[HOTEL_ID],'+
' lokacije.[NAZIV],'+
' uporabniki.[LOKACIJA_ID],'+
' uporabniki.[RESORT_ID],'+
' uporabniki.[HOTEL_ID],'+
' uporabniki.[UPORABNIK],'+
' uporabniki.[GESLO],'+
' uporabniki.[PRAVICE]'+
' from UPORABNIKI'+
' inner join LOKACIJE on uporabniki.lokacija_id=lokacije.lokacija_id '+
' where lokacije.[NAZIV] = :#NAZIV'+
' order by Uporabniki.[UPORABNIK]';
ClientDataSet1.Params.ParamByName('#NAZIV').Value:= '' + Form2.AdvOfficeStatusBar1.Panels[3].Text + '' ;
ClientDataSet1.Open;
end;
The query runs fine and gives me the desired results. However I would like to be able to edit and save the edited (or added) results of this query. The table that I want to update (or add the new record) is the UPORABNIKI. I do not need to write anything to the LOKACIJE table. How am I to accomplish this ?
Apart from saving new record I would like the query to fill automatically the values
LOKACIJA_ID,RESORT_ID,HOTEL_ID as they are from the same table, when I click in the navigator the button 'insert'. UPORABNIKI is translated USERS table.
edit : Inverted the query as suggested

I believe this is a case in which TDatasetProvider is unable to produce the right command to update the tables involved. In this cases, what I do is to add a handler to the TDatasetProvider.BeforeUpdateRecord event. This event will let you handle each operation worked on the dataset and produce the needed SQL statements to propertly persist those operations in the data server.
You will have to write the UPDATE/DELETE/INSERT statements yourself, but you will also have absolute power about how the tables are updated. That´s why I always use this event and never rely on TDatasetProvider intrinsec update process.

See the OnGetTableName event on the TDatasetProvider.
Also, I believe it would be better if you invert your query, i.e., use ... FROM UPORABNKI inner join LOKACIJE...

The OnGetTableName event from the TDataSetProvider has a var parameter called TableName. You should assign it to 'UPORABNIKI'.
Also, usually TDataSetProvider thinks that the table you use on the FROM clause is the one you want to update, so, if you change your query, the above event might not even be needed.
Hope that helps,
Marco

Related

Use new value in after trigger sql statement

I am trying to create a trigger that will dump out a csv file of data at a certain point in an application. When you create a payment order from a payment proposal then it means it is ready to be paid and uploaded to the bank. There is a wizard in the erp that makes the payment order from the payment proposal. There is also a header and a detail table on both proposals and orders. I need it to make it when there is a new row in the payment order table that has new.way_id = 'ACH' and new.institute_id = 'BMO'.
The problem is the wizard has multiple steps and doesn't insert the detail rows for the order till the last step when you click ok, but the header is created already before this and executes the trigger. Because of this when the header is created I am going to pull all of the data from the proposal header and detail because it is all already there.
When The trigger executes it makes a sql statement and then passes it to a stored procedure that will take any sql query and dump it to a csv file. For some reason it won't let me use the new reference when I create my query. I get an error saying "new.selected_proposals invalid identifier". I need it to do a like on this also because you can select multiple proposal header id's when you create an order and I only want it to do it for the ones from the proposals that have a way_id of ACH.
I am guessing I have to add the .new table, whatever it is, into the join or something, but I am not sure how to do that.
This is Oracle database 11g. Here is the code. The commented out section in the query is what I am trying to fix, just to give an idea of what I am trying to do.
create or replace TRIGGER CREATE_BMO_ACH_FILE
AFTER INSERT ON PAYMENT_ORDER_TAB
for each row
when (new.way_id = 'ACH' and new.institute_id = 'BMO')
declare sql_ varchar(4000);
BEGIN
sql_ := q'[select pp.company, pp.Proposal_id, pp.CREATION_DATE, pl.identity, pl.payee_identity, pl.ledger_item_id, pl.currency, pl.curr_amount, pl.GROSS_PAYMENT_AMOUNT, pl.PLANED_PAYMENT_DATE, pl.Order_Reference, pl.PAYMENT_REFERENCE
from payment_proposal pp, PROPOSAL_LEDGER_ITEM pl
where pp.company = pl.company
and pp.proposal_id = pl.proposal_id
and pp.way_id = 'ACH'
/*and pp.proposal_id like '%' || new.selected_proposals || '%'*/]';
dump_sql_to_csv( sql_, 'E:\Accounting', 'test.csv');
END;
I think your just missing a colon in front of the new i.e. use :new.selected_proposals. Colons aren't required for old and new in the WHEN but are in the code block.

Run a DELETE statement certain table names stored in a table

I have a table which stores the names of certain tables - tableNames. I'd like to run a DELETE statement on some of those tables (deleting all the rows from the tables they represent, not removing them from tableNames). I thought I could just do
DELETE FROM (SELECT tableName FROM tablesNames WHERE ...) AS deleteTables
But I keep getting an incorrect syntax error. I also thought about iterating through a table in a WHILE loop and storing using a variable, but that I'm hoping there's more simpler way. Specifically, this is for Microsoft SQL
You cannot do it that way because the inner SELECT is simply another set you're deleting from.
Basically you're creating a table of table names and telling the DB to delete it. Even iterating through them won't work without dynamic sql and EXEC
Do you need to automate this process?
What I've done in the past is something like this
SELECT
'DELETE ' + tableName
FROM
tablenames
WHERE
[conditions]
your output will look like this:
DELETE myTableName1
DELETE myTableName2
DELETE myTableName3
And then simply copying the results of this query out of the window and running them.
IF you need to automate this in SQL you can concatenate all the output strings in the result and send them as a parameter to an EXEC call.
try using cursor :
DECLARE #tableName varchar(255)
DECLARE cur cursor for select tableName from tableNames where (...)
OPEN CUR
FETCH NEXT FROM cur into #tableName
WHILE ##FETCH_STATUS = 0
BEGIN
exec('DELETE ' + #tableName)
FETCH NEXT FROM cur into #tableName
END
CLOSE cur
DEALLOCATE cur
In this respect, you can think of SQL as a compiled language like C/C++. The SQL statement is evaluated by a "compiler", and certain checks are done. One of those checks is for the existence (and permissions) for tables and columns referenced directly in the query. Exact table names must be present in your code at the time you build your query, so that the compiler can validate it.
The good news is that SQL is also a dynamic language. This means you can write a procedure to build a query as a string, and tell the database to execute that string using the EXEC command. At this point, all the same "compiler" rules apply, but since you were able to insert table names directly into your SQL string, the query will pass.
The problem is that this also has security implications. It would be a good idea to also check your table against a resource like information_schema.Tables, to avoid potential injection attacks. Unfortunately, if you're deleting whole tables your whole model may already be suspect, such that you can't guarantee that someone won't inject a table name that you really want to keep. But depending on how these are populated, you may also be just fine.
Assuming no potential constraint errors exist, one interesting possibility is an undocumented procedure sp_MSforeachtable, which will allow you to apply a given operation against all tables whose names are returned by your query:
EXEC sp_MSforeachtable #command1 = 'delete from ?'
, #whereand = 'and o.name IN (SELECT tableName FROM tablesNames WHERE ...)'
Also http://weblogs.asp.net/nunogomes/archive/2008/08/19/sql-server-undocumented-stored-procedure-sp-msforeachtable.aspx for more reading.
The delete statement works with only one table name at a time.
The full syntax is documented here, but it's TL;DR... In short, you'll have to use the loop.
I am using a similar cursor as #Pavel with a list of my indexes in order to reorganise them. Operations like this are one of the extremely few good reasons for cursors.

Debug Insert and temporal tables in SQL 2012

I'm using SQL Server 2012, and I'm debugging a store procedure that do some INSERT INTO #temporal table SELECT.
There is any way to view the data selected in the command (the subquery of the insert into?)
There is any way to view the data inserted and/or the temporal table where the insert maked the changes?
It doesn't matter if is the total rows, not one by one
UPDATE:
Requirements from AT Compliance and Company Policy requires that any modification can be done in the process of test and it's probable this will be managed by another team. There is any way to avoid any change on the script?
The main idea is that the AT user check in their workdesktop the outputs, copy and paste them, without make any change on environment or product.
Thanks and kind regards.
If I understand your question correctly, then take a look at the OUTPUT clause:
Returns information from, or expressions based on, each row affected
by an INSERT, UPDATE, DELETE, or MERGE statement. These results can be
returned to the processing application for use in such things as
confirmation messages, archiving, and other such application
requirements.
For instance:
INSERT INTO #temporaltable
OUTPUT inserted.*
SELECT *
FROM ...
Will give you all the rows from the INSERT statement that was inserted into the temporal table, which were selected from the other table.
Is there any reason you can't just do this: SELECT * FROM #temporal? (And debug it in SQL Server Management Studio, passing in the same parameters your application is passing in).
It's a quick and dirty way of doing it, but one reason you might want to do it this way over the other (cleaner/better) answer, is that you get a bit more control here. And, if you're in a situation where you have multiple inserts to your temp table (hopefully you aren't), you can just do a single select to see all of the inserted rows at once.
I would still probably do it the other way though (now I know about it).
I know of no way to do this without changing the script. Howeer, for the future, you should never write a complex strored proc or script without a debug parameter that allows you to put in the data tests you will want. Make it the last parameter with a default value of 0 and you won't even have to change your current code that calls the proc.
Then you can add statements like the below everywhere you will want to check intermediate results. Further in debug mode you might always rollback any transactions so that a bug will not affect the data.
IF #debug = 1
BEGIN
SELECT * FROM #temp
END

Do I need to perform a SELECT query after executing a Query to modify my record(s) in order to view the changed results?

Each time I perform a query (INSERT, DELETE,UPDATE). After Do I need to do Select * From Table, so my info can be seen on the Grid control?
For example:
UniQuery1 is my dataset.
I'm using a TDBADvListView control.
UniQuery1.Close;
UniQuery1.SQL.Clear;
SQL_QUERY:= 'insert into ListaCamiones(Tablilla,Marca,Modelo,Color) Values ('
+QuotedStr(a1)+','+
QuotedStr(a2)+','+
QuotedStr(a3)+','+
QuotedStr(a4)+')';
UniQuery1.SQL.Text := SQL_QUERY;
UniQuery1.Execute;
Do I need to do, Select * From ListaCamiones;
So I can see the information back on my TDBADvListView?
The answer is both yes and no!
Yes in that you do have to perform a SELECT query again in order to aggregate the modified recordset, no in that you don't have to perform the query as a separate execution.
If you append a semicolon at the end of your INSERT/UPDATE/DELETE query string, and immediately follow that by the desired SELECT query, your call to Execute will simultainiously update the records and aggregate the updated recordset for display.
Additionally, I would change the way you're building your SQL string too!
const
INSERT_QUERY_STRING = 'INSERT INTO ListaCaminoes(Tablilla, Marca, Modelo, Color) VALUES ("%s", "%s", "%s", "%s"); SELECT * FROM ListaCaminoes';
// Now inside your method
UniQuery1.SQL.Text := Format(INSERT_QUERY_STRING, [a1, a2, a3, a4]);
UniQuery1.Execute;
Hope it helps!
In general, yes, because in my experience when you make database changes via SQL statements:
no database component automatically refreshes the query,
no database can refresh the data in your application when the data
has changed in the database.
I recommend that you use a separate query component (UniQuery2) to execute your SQL statement. The you can use the ReQuery method of your Query to re-execute your original query (UniQuery1). Depending on the database components you are using, your local cursor may be reset.
Alternately you can Append/Insert to add records and Edit to change records of UniQuery1. This avoids the need to re-execute your original query because the changes are added to the dataset records buffered locally by the Query component. But, re-executing the query is necessary to get records that were added/edited by other users since your query was last executed.
If you just inserted the Information to the Database you have got it already!
In some SQL-Variants (in mySQL I am shure) you can have the command "insert_id()" from the API, that returns the AUTO_INCREMENT - value of the last inserted Dataset.
If you just want to get this ID, it is the way to go (on mySQL, like I said), but if you want to have other data you have to Query it again. In a combined query (like posted before) or in two seperate queries.
Glad to help!

Caching multiple versions of the same table

I have a problem with generating a particular table on the fly due to expensive SQL requests. I would like to pre-generate the table and simply display it to the user. The problem is: there are multiple versions of the table, and new ones will be continuously added.
Please give me some ideas on how to design a table (?) to hold these tables.
One idea that I have is to append a version number to each row in the individual tables and dump them all into a single cache table. This way, I can easily display just the requested version by filtering on version number. Is there a better way?
Without an example I may be making some assumptions here, but it sounds to me like dynamic SQL would do the trick.
declare #sql varchar(max)
select
#sql = 'SELECT * FROM MyTableName_' + v.version
from dbo.Version as v
where v.id = 1
exec (#sql)
If you end up using it, just know that dynamic sql is a cruel mistress. With one hand she give'th, the other she take'th away.