Output Parameters used with update CASCADE issue, when using data adapters - .net-4.0

I've manually created some data adapters – using the auto-generated ones is not viable due to version incompatibilities, for a dataset made of a number of tables with the usual mixture of PK, FK constraints. Most of its working pretty smoothly so far, but after modifying the adapters to use DB sequences for the PKs (instead of the temporary one assigned to the rows in the dataset) when updating the DB with new ‘added’ rows, I’ve been hitting problems.
I added the sequences into the insert statements and changed PK parameter to output parameters, so that it would update the dataset row and thus update all the child rows as well (with the UPDATE CASCADE rule). The problem is that the child rows, that, before the update had a row state of added, are changed to a state of modified (I don’t agree this should even be happening, surely an added row should stay as added even if it’s modified!). Thus when we get round to updating the child table with the child rows, it fails as its expecting rows with the added state.
What’s the cleanest way I could get around this problem? Potential solutions I can think of:
Turning off UPDATE CASCADE and updating each child row manually after, updating the parents PK and changing each row back to added, after modifying it.
Creating a copy of the all the added rows in all tables of the dataset before starting updates and then updating the main copy after each tables updates marking all the correct rows back to added.
Any better ideas?

Related

Filtering out DataRows that don't have Parents

I have a two tables in a DataSet that have a Parent-Child relation. The Parent is built from external data that I don't have control over, while the Child is sourced from the database that I am using for my internal data. This means that I can't always be certain that there will be a Parent for my Children. If this happens, I would prefer to filter out the Children from the DataGridView that is consuming the data via a BindingSource; I don't want to remove the rows from the database because settings changes (or other events I have no control over) might reintroduce rows that were previously present and removed.
I had previously had to figure out how to go the opposite way, to create Children for previously unencountered Parents via:
dim newrows() as ParentRow = ParentTable.Select("Count(Child(MyRelation).ForeignKeyColumn) < 1")
For Each row as ParentRow in newrows
ChildRow.AddChildRow(row, otherData)
Next
I thought I could use a similar approach:
BindingSource.Filter = "PARENT(MyRelation).KeyColumn IS NOT NULL"
But this filters out everything. Investigating, I discovered that running
ChildTable.Select("PARENT(MyRelation).KeyColumn IS NULL")(0).GetParentRow("MyRelation").Item("KeyColumn")
on a table where the given result has a parent succeeds and gives a value, which seems to contradict the Select statement, so clearly something unexpected is happening with the Select statement.
I am at a loss. Is there any way to filter out (but retain in the backend) rows that don't have a parent via the BindingSource? What am I not seeing here?
Answer
It turns out that making a Boolean column with an expression that calculates Parent.Key IS NOT NULL works and is filterable by the BindingSource, without throwing a flag for the row having been changed.
Possible Explanation
Combined with the observation that the original method only fails when constraints are turned off, this makes me think that this might be a design decision by Microsoft in light of the fact that, when constraints are off, there is no guarantee that a Child will only have one Parent. So the filtering by a Parent column will fail when the constraints are off, but filtering by a column calculated by the Parent column doesn't care about the constraints and so is okay. I just have to do my own work to make sure that there is only one Parent; fortunately this is guaranteed by the data source I am generating the Parent from.

Required an expert suggestion in TSQL

I have a scenarios where Table A has reference to Table B , Table B has a reference on Table C column ...etc.
To implement an update task in my project I ought to implement it in two phase logic
i.e. delete the row first and add the latest again.
But unfortunately , when I try to delete a row in table A it has reference which in turn has reference to other table and so on. Hence my logic of delete and add does not work in a proper way all the time. Even if it is deleted and added again , the sequence at which it is being added is last i.e. as a new record. Hence I am losing all the earlier references track in the same order as old one.
Hence I would like to delete a row from a table without effecting the references i.e. for time being it should allow to ignore reference , once i added it again i.e update record then i need to re-enforce/enable back the reference.
Is it possible to do in such a way ? or there any other logic works in similar fashion or replace the original intention ? could anyone please provide your expertise advice on this ?
How general logic of windows service pack works ? can any one elaborate on that? or share some info or doc or blog regarding the same?
Thank you so much.
Regards,
Shyam
What you want to do is a bad practice, I would rethink your design. It doesn't let you delete the parent record because there are child records. That is what the database ii supposed to and to try to circumvent it is a 100% guarantee of bad data.
If what you are trying to accomplish is to move the child records to a new parent, that can be done but you add the new record first and then make updates. It is best if you have some field to be able to define what old record it used to be associated with or a mapping table to use to make many changes. Then you would need to run updates for every child table. This kind of thing shoudl be a one time change, not a regular practice. It certainly shoudl virtually never happen from the application and shoudl only be done by a qualified databse developer.
If what you are trying to accomplish is to inactivate the parent so it can no longer be used for some purposes(such as creating new orders) and leave the details for reporting (wouldn't want to lose the finacials for old orders), then you should put an active flag on the table and use that to filter records instead. Often this means creating a view of only active records and pointing the code to the view insted of directly to the table.

SQL DELETE appears to work but then does not implement changes

I have created a table of Cards that so far contains 10 rows. if I execute:
delete from [Card] where cardnumber = 00010 //this card being held in the last row
I get the resulting message (1 row(s) affected), which I would expect works, however upon opening the table I find that the row is still there.
Next I used Edit Top 200 Rows in SSMS to select the row manually and delete it, upon which it is removed from the table, but when I execute, the row is back in the table.
Finally, I created a stored procedure that returns a success when executed, but the changes are still not being kept.
What is strange however, is that I have a CardAmount table with a relationship to the Card table that holds points per card, and the cards are being removed from CardAmount.
I have had a look at this question, but I don't think that was ever resolved.
First guess (which turned out to be correct) would be an instead of delete trigger that forgot to actually affect the table it's meant to be performing the delete for.
This is a common error, especially if you're implementing some far reaching form of cascade (and can't just use ON DELETE CASCADE on the foreign key constraint) where you have to use an INSTEAD OF trigger but then neglect to perform the DELETE on the underlying table.

Sql Serve - Cascade delete has multiple paths

I have two tables, Results and ComparedResults.
ComparedResults has two columns which reference the primary key of the Results table.
My problem is that if a record in Results is deleted, I wish to delete all records in ComparedResults which reference the deleted record, regardless of whether it's one column or the other (and the columns may reference the same Results row).
A row in Results may deleted directly or through cascade delete caused by deleting in a third table.
Googling this could indicate that I need to disable cascade delete and rewrite all cascade deletes to use triggers instead. Is that REALLY nessesary? I'd be prepared to do much restructuring of the database to avoid this, as my main area is OO programming, and databases should 'just work'. It is hard to see, however, how a restructuring could help as I would just move the problem around... Or am I missing something?
I am also a bit at a loss as to why my initial construct should even be a problem for the Sql Server?!
Any comments welcome and much appreciated!
Anders, Denmark
Would it be possible to split the ComparedResults into two tables?
Edit:
You could then use a View to gather the results for showing? Or a join between the two tables?

updating primary key of master and child tables for large tables

I have a fairly huge database with a master table with a single column GUID (custom GUID like algorithm) as primary key and 8 child tables that have foreign key relationships with this GUID column. All the tables have approximately 3-8 million records. None of these tables have any BLOB/CLOB/TEXT or any other fancy data types just normal numbers, varchars, dates, and timestamps (about 15-45 columns in each table). No partitions or other indexes other than the primary and foreign keys.
Now, the custom GUID algorithm has changed and though there are no collisions I would like to migrate all the old data to use GUIDs generated using the new algorithm. No other columns need to be changed. Number one priority is data integrity and performance is secondary.
Some of the possible solutions that I could think of were (as you will probably notice they all revolve around one idea only)
add new column ngu_id and populate with new gu_id; disable constraints; update child tables with ngu_id as gu_id; renaname ngu_id->gu_id; re-enable constraints
read one master record and its dependent child records from child tables; insert into the same table with new gu_id; remove all records with old gu_ids
drop constraints; add a trigger to the master table such that all the child tables are updated; start updating old gu_id's with new new gu_ids; re-enable constraints
add a trigger to the master table such that all the child tables are updated; start updating old gu_id's with new new gu_ids
create new column ngu_ids on all master and child tables; create foreign key constraints on ngu_id columns; add update trigger to the master table to cascade values to child tables; insert new gu_id values into ngu_id column; remove old foreign key constraints based on gu_id; remove gu_id column and rename ngu_id to gu_id; recreate constraints if necessary;
use on update cascade if available?
My questions are:
Is there a better way? (Can't burrow my head in the sand, gotta do this)
What is the most suitable way to do this? (I've to do this in Oracle, SQL server and mysql4 so, vendor-specific hacks are welcome)
What are the typical points of failure for such an exercise and how to minimize them?
If you are with me so far, thank you and hope you can help :)
Your ideas should work. the first is probably the way I would use. Some cautions and things to think about when doing this:
Do not do this unless you have a current backup.
I would leave both values in the main table. That way if you ever have to figure out from some old paperwork which record you need to access, you can do it.
Take the database down for maintenance while you do this and put it in single user mode. The very last thing you need while doing something like this is a user attempting to make changes while you are in midstream. Of course, the first action once in single user mode is the above-mentioned backup. You probably should schedule the downtime for some time when the usage is lightest.
Test on dev first! This should also give you an idea as to how long you will need to close production for. Also, you can try several methods to see which is the fastest.
Be sure to communicate in advance to users that the database will be going down at the scheduled time for maintenance and when they can expect to have it be available again. Make sure the timing is ok. It really makes people mad when they plan to stay late to run the quarterly reports and the database is not available and they didn't know it.
There are a fairly large number of records, you might want to run the updates of the child tables in batches (one reason not to use cascading updates). This can be faster than trying to update 5 million records with one update. However, don't try to update one record at a time or you will still be here next year doing this task.
Drop indexes on the GUID field in all the tables and recreate after you are done. This should improve the performance of the change.
Create a new table with the old and the new pk values in it. Place unique constraints on both columns to ensure you haven't broken anything so far.
Disable constraints.
Run an updates against all the tables to modify the old value to the new value.
Enable the PK, then enable the FK's.
It's difficult to say what the "best" or "most suitable" approach is as you have not described what you are looking for in a solution. For example, do the tables need to be available for query while you are migrating to new IDs? Do they need to be available for concurrent modification? Is it important to complete the migration as fast as possible? Is it important to minimize the space used for migration?
Having said that, I would prefer #1 over your other ideas, assuming they all met your requirements.
Anything that involves a trigger to update the child tables seems error-prone and over complicated and likely will not perform as well as #1.
Is it safe to assume that new IDs will never collide with old IDs? If not, solutions based on updating the IDs one at a time will have to worry about collisions -- this will get messy in a hurry.
Have you considered using CREATE TABLE AS SELECT (CTAS) to populate new tables with the new IDs? You'll be making a copy of your existing tables and this will require additional space, however it is likely to be faster than updating the existing tables in place. The idea is: (i) use CTAS to create new tables with new IDs in place of the old, (ii) create indexes and constraints as appropriate on the new tables, (iii) drop the old tables, (iv) rename the new tables to the old names.
In fact, it depend on your RDBMS.
Using Oracle, the simpliest choice is to make all of the foreign key constraints "deferred" (check on commit), perform updates in a single transaction, then commit.