ASP.NET SqlDataSource update and create FK reference - asp.net

The short version:
I have a grid view bound to a data source which has a SelectCommand with a left join in it because the FK can be null. On Update I want to create a record in the FK table if the FK is null and then update the parent table with the new records ID. Is this possible to do with just SqlDataSources?
The detailed version:
I have two tables: Company and Address. The column Company.AddressId can be null. On my ascx page I am using a SqlDataSource to select a left join of company and address and a GridView to display the results. By having my UpdateCommand and DeleteCommand of the SqlDataSource execute two statements separated by a semi-colon I am able to use the GridView's Edit and Delete functionality to update both table simultaneously.
The problem I have is when the Company.AddressId is null. What I need to have happen is have the data source create a record in the Address table and then update the Company table with the new Address.ID then proceed with the update as usual. I would like to do this with just data sources if possible for consistency/simplicity sake. Is it possible to have my data source do this, or perhaps add a second data source to the page to handle some of this?
Once I have that working I can probably figure out how to make it work with the InsertCommand as well but if you are on a roll and have an answer for how to make that fly as well feel free to provide it.
Thanks.

execute two statements separated by a
semi-colon
I don't see any reason why it wouldn't be possible to do both an INSERT and UPDATE in two statements with SqlDataSource just like you are doing here.
However, just so you know, if you have a lot of traffic or users using the application at the same time, you can run into concurrently issues where one user does something that affects another user and unexpected results can cascade and mess up your data. In general, for things like what you are doing - INSERT and UPDATE involving primary or foreign keys, usually SQL TRANSACTIONs are used. But, you must execute them as SQL stored procedures (or functions), on your SQL database. You are still able to call them from your SqlDataSource however by simply telling it that you are calling a stored procedure.

Related

how to insert into 2 table using join in sql server?

I have 2 Tables :
T_Customer
T_Address
In T_Customer Table have T_Address Foreign Key.
on Web Page Created form Where Customer can add there Details like First Name, Last Name, Address etc.
When Customer Submit form then Adddress details should insert in T_Address table And T_Address ID goes To T_Customer Table is it possible to Create 2 insert operations on one button click?
Is Joins is possible in this?
A single INSERT statement can only affect one table, albeit that you might have some triggers that in turn affect other tables. However, your question was specifically about an action on one button click. This is very possible, and indeed highly advisable. Simply create yourself a single stored procedure and pass it as parameters all the fields required for both Customer and Address tables.
Then within your Stored procedure you can have two inserts, one to Customers and one to Address. If you have a referential integrity constraint, then you must insert customers before address.
The advantage of this approach, is that all commands within a stored procedure are covered by the same transaction: it all succeeds or all fails, and requires only one round trip to the database, in turn being quicker and using less resources.

Teradata: Is there a way to generate DDL from a view or select statement?

I am using a global application user account to access database A. This user account does not have permissions to modify database A's schema (ie, create tables, modify tables, etc). This user also has access to database B, but only views. I need to run SQL to feed data from a view in database B into a table in database A.
In a perfect world, I would be able to use this SQL:
create database_a.mytable as (select * from database_b) with no data
However, the user can't create tables in database A. If I could get the DDL of the select statement then I could log in under my personal account (which doesn't have any access to database B) and run the DDL in database A to create the table.
The only other option is to manually write the SQL, but I don't want to do that, especially since this view I am wanting to copy has many columns of varying data types and sizes.
Edit: I may be getting closer. I just experimented with this:
show (select * from database_b.myview)
However, it generated the DLL of every single table that is used in the view itself, as well as the definition for the view. This doesn't really help me since I just want the schema of the select statement itself. In other words, I need what would be generated if I were to use the create table as statement mentioned above.
Edit for Rob: Perhaps "DDL" was the wrong term to use. Using show view db.myview just shows the definition of the view, not the schema it represents. In my above example of create table as, I show how you can create a table that mimics the schema of a result set returned in a select. It generates a DDL on the back end for creating a table and then executes that DDL to actually create the table. You can then say show table db.newtable and see the new table's DDL. I want to get that DDL directly from a select statement so that I can copy it, log out of the app account, into my personal account, and then execute the DDL to create the table.
This is only to save me the headache of having to type out the DDL manually by hand to save time and reduce typing errors, especially since the source view has so many columns. That said, I think hitting up the DBA or writing some snazzy stored procedure to do dynamic stuff would be a bit over the top for my needs. I think there has to be a way to get the DDL for creating a table schema directly from a select statement.
Generate DDL Statements for objects:
SHOW TABLE {DatabaseB}.{Table1};
SHOW VIEW {DatabaseB}.{View1};
Breakdown of columns in a view:
HELP VIEW {DatabaseB}.{View1};
However, without the ability to create the object in the target database DatabaseA your don't have much leverage. Obviously, if the object already existed INSERT INTO SELECT ... FROM DatabaseB.Table1 or MERGE INTO would be options that you already explored.
Alternative Solution
Would it be possible to have a stored procedure created that dynamically created the table based on the view name that is provided? The global application account would simply need privilege to execute the procedure. Generally the user creating the stored procedure would need the permissions to perform the actions contained within the stored procedure. (You have some additional flexibility with this in Teradata 13.10.)
There are some caveats with this approach. You are attempting to materialize views that could reference anywhere from hundreds to billions of records. These aren't simple 1:1 views that are put on top of the target tables. Trying to determine the required space in the target database to materialize the view will be difficult. Performance can and will vary depending on the complexity of the view and the data volumes. This will not be a fast-path or data block optimized operation.
As a DBA, I would be concerned with this approach being taken on by a global application account without fully understanding the intent. I trust you have an open line of communication with the DBA(s) involved for supporting this system. I'm sure there are reasons for your madness that can't be disclosed here.
Possible Solution - VOLATILE TABLE
Unless the implicit privilege for CREATE TABLE has been revoked from the global application account this solution should work.
Volatile tables do not require perm space. There table definitions persist for the duration of the session and any data inserted into them relies on the spool space of the user who instantiated it.
CREATE VOLATILE TABLE {Global Application UserID}.{TableA_Copy} AS
(
SELECT *
FROM {DatabaseB}.{TableA}
)
WITH NO DATA
NO PRIMARY INDEX
ON COMMIT PRESERVE ROWS;
SHOW TABLE {Global Application UserID}.{TableA_Copy};
I opted to use a Teradata 13.10 feature called NO PRIMARY INDEX. By default, CREATE TABLE AS will take the first column of the SELECT statement and make it the PRIMARY INDEX of the table. This could lead to skewing and perm space issues in your testing depending on the data demographics. You can specify an explicit PRIMARY INDEX on your own as you understand the underlying data. (See the DDL manuals for details on the syntax if you're uncertain.)
The use of ON COMMIT PRESERVE ROWS for the intent of this example is probably extraneous. But in reality if you popped any data into that table for testing this clause would be beneficial in Teradata mode as the data would otherwise be lost immediately after the CREATE TABLE or any other data manipulation was performed against the volatile table.

GridView does not load schema from the SQL stored Procedure

I have gridview, when I connect the sqldatasource and run the application, data from stored procedure is shown but when i switch to design mode, the schema does not loads in gridview. I need that schema to rearrange columns and for formatting purpose.
I never had this problem before, All of sudden it is a problem.
What I tried so far "Refresh Schema" brings up the dialog which list all the parameters I am passing to the storedprocedure. After I changed DBType for every single parameter, it still give me an error, says "check your connection and storedprocedure". While the storedprodure does run, shouldn't it automatically update the schema in gridview. I still have the default 3 columns of gridview.
Note: I may get around the problem by adding each column manually but I am doing this to fix problem in the page, in the first place, dont want to run again in gridview problem. This is a new page that I am building. Thanks
It looks like, Schema is not updated just from StoredProcedure because simple there is not enough information how many columns will be returned.
To work around this issue and sort of quick fix, do the following.
Copy the query from you stored procedure and change the SQLDatasource from stored procedure to Select statement. Run your select statement and finish. Notice that your schema will now be updated.
You can now set your SQLDataSource back to stored procedure and the schema will remain the same.
It also occured to me that when you run a stored procedure, the column names are updated in the query using the name 'Databound col0' 'Databound Col1' and so on
I am not sure sometime it does, sometime it does not. Right click on Gridview and click Referesh Schema does not work as it gives some error.
Along the lines of #hmd, when there is not enough info, especially if there is another perceived dataset being return, i.e you are doing something that returns blank or debug data (rows affected, etc.). A workaround for this is to put "SET NOCOUNT OFF" in the beginning of your procedure and then set it back "ON" again before you return the data. This is also a problem on SSRS.

asp.net InsertCommand to return latest insert ID

I'm unable to retrieve the latest inserted id from my SQL Server 2000 db using a typed dataset in asp.NET
I have created a tableadapter and I ticked the "Refresh datatable" and "Generate Insert, Update and Delete statements". This auto-generates the Fill and GetData methods, and the Insert, Update, Select and Delete statements.
I have tried every possible solution in this thread
http://forums.asp.net/t/990365.aspx
but I'm still unsuccesfull, it always returns 1(=number of affected rows).
I do not want to create a seperate insert method as the auto-generated insertCommand perfectly suits my needs.
As suggested in the thread above, I have tried to update the InsertCommand SQL syntax to add SELECT SCOPY_IDENTITY() or something similar, I have tried to add a parameter of type ReturnValue, but all I get is the number of affected rows.
Does anyone has a different take on this?
Thanks in advance!
Stijn
I decided to give up, I can't afford to waste any more time on this.
I use the Insert statement after which I do a select MAX(id) query to hget the insert ID
If anyone should have a solution, I'll be glad to read it here
Thanks
Stijn
I successfully found a way to get the incremental id after insert using my table adapter.
My approach is a little different, I'm using a Store procedure to make the insert, so my insert command has all the values but the ID, I made the sp return the ID just calling:
SET #ID=SCOPE_IDENTITY()
and then
COMMIT TRAN
and last line will be
RETURN #ID
Then I searched my table adapter parameters for InsertCommand and set the #RETURNVALUE to the column of the incremental ID of the table, so when it's executed automatically put the return value on the id field.
Hope this help
You need to tell your table's table-adapter to refresh the
data-table after update/insert operation.
This is how you can do that.
Open the properties of TableAdapter -> Default Select Query -> Advnaced options. and Check the option of Refresh the data table. Save the adapter now. Now when you call update on table-adapter, the data-table will be updated [refreshed] after the update/insert operation and will reflect the latest values from database table. if the primary-key or any coloumn is set to auto-increment, the data-table will have those latest value post recent update.
Now you can Call the update as TableAdapterObj.Update(ds.dataTable);
Read latest values from the DataTable(ds.dataTable) coloumns and assign respective values into the child table before update/insert. This will work exactly the way you want.
alt text http://ruchitsurati.net/files/tds1.png

How to Get Last Created Entry's ID From Sql Database With Asp.Net

I will explain problem with an example:
There is two table in my database, named entry, tags
There is a column named ID_ENTRY in both table. When I add a record to table, entry, I have to take the ID_ENTRY of last added record and add it to table, tags. How can I do it?
The only way to do this is with multiple statements. Using dynamic sql you can do this by separating each statement in your query string with a semi-colon:
"DECLARE #ID int;INSERT INTO [Entry] (...) VALUES ...; SELECT #ID = scope_identity();INSERT INTO [TAGS] (ID_ENTRY) VALUES (#ID);"
Make sure you put this in a transaction to protect against concurrency problems and keep it all atomic. You could also break that up into two separate queries to return the new ID value in the middle if you want; just make sure both queries are in the same transaction.
Also: you are using parameterized queries with your dynamic sql, right? If you're not, I'll personally come over there and smack you 10,000 times with a wet noodle until you repent of your insecure ways.
Immediatly after executing the insert statement on first table, you should query ##IDENTITY doing "SELECT ##identity". That will retrieve the last autogenerated ID... and then just insert it on the second table.
If you are using triggers or something that inserts rows... this may be not work. Use Scope_Identity() instead of ##IDENTITY
I would probably do this with an INSERT trigger on the named entry table, if you have all of the data you need to push to the tags table available. If not, then you might want to consider using a stored procedure that creates both inside a transaction.
If you want to do it in code, you'll need to be more specific about how you are managing your data. Are you using DataAdapter, DataTables, LINQ, NHibernate, ...? Essentially, you need to wrap both inserts inside a transaction of some sort so that either inserts get executed or neither do, but the means to doing that depend on what technology you are using to interact with the database.
If you use dynamic sql, why not use Linq to Entity Framework, now EF is the recommend data access technology from Microsoft (see this post Clarifying the message on L2S Futures from ADO.NET team blog), and if you do an insert with EF the last identity id will available for you automatically, I use it all the time it's easy.
Hope this helps!
Ray.

Resources