Insert & Delete from SQL best practice - sql

I have a database with 2 tables: CurrentTickets & ClosedTickets. When a user creates a ticket via web application, a new row is created. When the user closes a ticket, the row from currenttickets is inserted into ClosedTickets and then deleted from CurrentTickets. If a user reopens a ticket, the same thing happens, only in reverse.
The catch is that one of the columns being copied back to CurrentTickets is the PK column (TicketID)that idendity is set to ON.
I know I can set the IDENTITY_INSERT to ON but as I understand it, this is generally frowned upon. I'm assuming that my database is a bit poorly designed. Is there a way for me to accomplish what I need without using IDENTITY_INSERT? How would I keep the TicketID column autoincremented without making it an identity column? I figure I could add another column RowID and make that the PK but I still want the TicketID column to autoincrement if possible but still not be considered an Idendity column.

This just seems like bad design with 2 tables. Why not just have a single tickets table that stores all tickets. Then add a column called IsClosed, which is false by default. Once a ticket is closed you simply update the value to true and you don't have to do any copying to and from other tables.
All of your code around this part of your application will be much simpler and easier to maintain with a single table for tickets.

Simple answer is DO NOT make an Identity column if you want your influence on the next Id generated in that column.
Also I think you have a really poor schema, Rather than having two tables just add another column in your CurrentTickets table, something like Open BIT and set its value to 1 by default and change the value to 0 when client closes the Ticket.
And you can Turn it On/Off as many time as client changes his mind, with having to go through all the trouble of Insert Identity and managing a whole separate table.
Update
Since now you have mentioned its SQL Server 2014, you have access to something called Sequence Object.
You define the object once and then every time you want a sequential number from it you just select next value from it, it is kind of hybrid of an Identity Column and having a simple INT column.

To achieve this in latest versions of SQL Server use OUTPUT clause (definition on MSDN).
OUTPUT clause used with a table variable:
declare #MyTableVar (...)
DELETE FROM dbo.CurrentTickets
OUTPUT DELETED.* INTO #MyTableVar
WHERE <...>;
INSERT INTO ClosedTicket
Select * from #MyTableVar
Second table should have ID column, but without IDENTITY property. It is enforced by the other table.

Related

Informix select trigger to update column

Is it possible to increase the value of a number in a column with a trigger every time it gets selected? We have special tables where we store the new id and when we update it in the app, it tends to get conflicts before the update happens, even when it all takes less than a second. So I was wondering if it is not possible to set database to increase value after every select on that column? Do not ask me why we do not use autoincrement for ids because I do not know.
Informix provides the SERIAL and BIGSERIAL types (and also SERIAL8, but don't use that) which provide autoincrement support. It also provides SEQUENCES with more sophisticated autoincrements. You should aim to use one of those.
Trying to use a SELECT trigger to update the table being selected from is, at best, fraught with problems about transactions and the like (problems which both the types and sequences carefully avoid).
If your design team needs help making effective use of these, ask a new question outlining what you want to achieve.
Normally, the correct way to proceed is to make the ID column in each table that defines 'something' (the Orders table, the Customer table, …) into a SERIAL column and either not insert a value into the ID column or insert 0 into it. The generated value can be retrieved and used when creating auxilliary information — order items, etc.
Note that you could think about using:
CREATE TABLE xyz_sequence
(
xyz SERIAL NOT NULL PRIMARY KEY
);
and using:
INSERT INTO xyz_sequence VALUES(0);
and then retrieving the inserted value — in Informix ESQL/C, you'd use sqlca.sqlerrd[1], in other languages, other techniques. You can also delete the newly inserted record, or even all the records in the table. You can afford to ignore errors from the DELETE statement; sooner or later, the rows will be deleted. The next value inserted will continue where the prior ones left off.
In a stored procedure, you'd use DBINFO('sqlca.sqlerrd1') to get the inserted value. You'd use DBINFO('bigserial') to get the value if you use a BIGSERIAL type.
I found out possible answer in this question update with return value instead of doing it with select it seems better to return value directly from update as update use locks it should be more safer even when you use multithreading application. But these are just my assumptions. Hopefully it will help someone.

rebuild/refresh my table's PK list - gap in numbers

I have finished all my changes to a database table in sql server management studio 2012, but now I have a large gap between some values due to editing. Is there a way to keep my data, but re-assign all the ID's from 1 up to my last value?
I would like this cleaned up as I populate dropdownlists with these values and then I make interactions with my database with the assumption that my dropdownlist index and the table's ID match up, which is not the case right now.
My current DB has a large gap from 7 to 28, I would like to shift everything from 28 and up, back down to 8, 9, 10, 11, ect... so that my database has NO gaps from 1 and onward.
If the solution is tricky please give me some steps as I am new to SQL.
Thank you!
Yes, there are any number of ways to "close the gaps" in an auto generated sequence. You say you're new to SQL so I'll assume you're also new to relational concepts. Here is my advice to you: don't do it.
The ID field is a surrogate key. There are several aspects of surrogates one must be mindful of when using them, but the one I want to impress upon you is,
-- A surrogate key is used to make the row unique. Other than the guarantee that
-- the value is unique, no other assumptions may be made concerning the value.
-- In particular, no meaning may be derived from the value as to the contents of
-- the row or the row's relationship to any other row.
You have designed your app with a built-in assumption of the value of the key field (that they will be consecutive). Already it is causing you problems. Do you really want to go through this every time you make changes to the table? And suppose a future feature requires you to filter out some of the choices according to an option the user has selected? Or enable the user to specify the order of the items? Not going to be easy. So what is the solution?
You can create an additional (non-visible) field in the dropdown list that contains the key value. When the user makes a selection, use that index to get the key value of the selection and then go out to the database and get whatever additional data you need. This will work if you populate the list from the entire table or just select a few according to some as yet unknown filtering criteria or change the order in any way.
Viola. You never have this problem again, no matter how often you add and remove rows in the table.
However, on the off chance that you are as stubborn as me (not likely!) or just refuse to listen to the melodious voice of reason and experience, then try this:
Create a new table exactly like the old table, including auto incrementing PK.
Populate the new table using a Select from the old table. You can specify any order you want.
Drop the old table.
Rename the new table to the old table name.
You will have to drop and redefine any FKs from other tables. But this entire process
can be placed in a script because if you do this once, you'll probably do it again.
Now all the values are consecutive. Until you edit the table again...
You should refactor the code for your dropdown list and not the PK of the table.
If you do not agree, you can do one of the following:
Insert another column holding the dropdown's "order of appearance", make a unique index on it and fill this by hand (or programmatically).
Replace the SERIAL with an INT would work, make a unique index on the column and fill this by hand (or programmatically).
Remove the large ids and reseed your serial - the code depending on your DBMS
This happens to me all the time. If you don't have any foreign key constraints then it should be an easy fix.
Remember a DELETE statement will remove the record but keep the identity seed the same. (If I remove id # 5 and #5 was the last record inserted then SQL server still stores the identity seed value at "6").
TRUNCATING the table will reset the identity seed back to it's original value.
INSERT_IDENTITY [TABLE] ON can also be used to insert the correct data in the correct order if tuncating cannot happen.
SELECT *
INTO #tempTable
FROM [TableTryingToFix]
TRUNCATE TABLE [TableTryingToFix];
INSERT INTO [TableTryingToFix] (COL1, COL2, COL3, ETC)
SELECT COL1, COL2, COL2, ETC
FROM #tempTable
ORDER BY oldTableID

How do I use ONLY ONE sql query for conditional insert?

I am using swing menu for my Java app. I also have MySQL database. There's a column (named brind) in one of the tables in the database which has to have unique values (other than ID column where values are auto inserted). So through text fields on my JFrame form I will try to insert some values in that table but only if that brind value does not already exist in the table (that is, in the column). I need to have ONLY ONE mysql query for this, call it conditional insert. How do I do this?
Thanks
A query like the following should do what you are asking:
INSERT INTO `thetable`
SELECT 'values', 'to', 'insert'
WHERE NOT EXISTS (
SELECT *
FROM `thetable`
WHERE brind='whatever'
)
Where values,to,insert should be replaced with the values of the fields you are inserting.
However, you would be better served by some of the other suggestions here, e.g.
Check for the value first and don't insert if it exists (do this within one transaction to handle concurrency issues, presuming your transaction isolation is set appropriately),
Attempt the insert and handle the unique constraint violation failure.
Option 2 is a good option, in my opinion.
REPLACE INTO myTable
SET brind = 'someValue'
I think you have a database design problem. If the column brind, has to be unique, the it should probably serve as the primary key.
will try to insert some values in that table but only if that brind
value does not already exist in the table (that is, in the column)
That can be done in database level and application level.
As database level is simplest , safest. Make an index on that column and make it unique (and not identifier!) and the database will not insert if is already exist that value. Here is how to do it.
The application level: you will cache what has the database and you will lock the table, disallowing other apps to modify it. Insert it if you want and unlock table.
I would suggest the first one solution

Replace identity column from int to bigint

I am using SQL Server 2008, and I have a table that contains about 50 mill rows.
That table contains a primary identity column of type int.
I want to upgrade that column to be bigint.
I need to know how to do that in a quick way that will not make my DB server unavailable,
and will not delete or ruin any of my data
How should I best do it ? what are the consequences of doing that?
Well, it won't be a quick'n'easy way to do this, really....
My approach would be this:
create a new table with identical structure - except for the ID column being BIGINT IDENTITY instead of INT IDENTITY
----[ put your server into exclusive single-user mode here; user cannot use your server from this point on ]----
find and disable all foreign key constraints referencing your table
turn SET IDENTITY_INSERT (your new table) ON
insert the rows from your old table into the new table
turn SET IDENTITY_INSERT (your new table) OFF
delete your old table
rename your new table to the old table name
update all table that have a FK reference to your table to use BIGINT instead of INT (that should be doable with a simple ALTER TABLE ..... ALTER COLUMN FKID BIGINT)
re-create all foreign key relationships again
now you can return your server to normal multi-user usage again
What am I missing?
Why can't you just do this:
ALTER TABLE tableName ALTER COLUMN ID bigint
I guess try it in a test environment first but this always works for me
Probably the best way is to create a new table with a BIGINT IDENTITY column, move the existing data using SET IDENTITY_INSERT ON; and then rename the tables. You will need to do this during a maintenance window, just as you would if you changed the data type in Management Studio (which would similarly create a new table, move the data, and block everyone in the process).
You could use Alter script for your column as #MobileMon said, but couldn't do this before removing constraints. And besides FK constraints, you must also remove PK constraint before changing the column type!
Also there is another creative way, if the ID data is not important (No FK etc):
Take a Backup of table (if it's in a separate FileGroup) or DB
Rename table (Having no more inserts)
Remove PK/Constraints from the column
Drop ID column
Add new ID column, with Identity
Apply PK
Rename table back to original name (back to work :) )
& If the ID data is important:
Step 1,2 like above
Create a new column
Transfer the data from the existing IDENTITY column to the new column
Drop the existing IDENTITY column & PK.
Make new column, with Identity
Apply PK
Rename table back to original name (back to work :) )
Important Note: 1. If the old column ID value in not important & there are big gaps between your values(you have deletes besides inserts), you don't need BigInt. Just make the new ID column as Int again.
2. When table grows & is reaching Overflow value(2 billion) you could look at the actual row number in properties, storage of your table. Maybe your reaching overflow, but your row number is much less than that.
Why would someone want to use a BigInt instead of Int as an IDENTITY?
Consider this scenario:
Your database exists in several environments including 1 instance in a live Production environment and several other instances in (TestA, B, C, etc.), (QA A, B, C, etc.), (Demo A, B, C, etc), (UAT A, B, C, etc.), (Training A, B, C, etc.) on and on and on... You don't even want to know...
This database IDENTITY field is used to pass in an unique number to a 3rd party provider which is a shared environment in the Non Production environments. The vendor charges an arm and a leg in order to set up multiple environments so the company has one for the production DB and one for ALL the others.
So... when testing happens in the non production environments these numbers can never cross each other from whatever non production environment you happen to be testing in. And the testing includes stress testing... sending 100's of thousands of rows at a time.
To top it off... ALL these environments get refreshed with Production so the Identity field gets reset with whatever was in production. So one has to keep track of what spread was used in each environment and then reset the IDENTITY to a new spread that has never been used before. The 3rd party vendor will puke if an already number gets sent again in these environments. And the vendor is unwilling or unable to refresh or reset these numbers on their end.
This is a real world issue and the current field remains to be an int in ALL environments and the management of keeping track of these spreads is updated every quarter or whenever someone does a massive stress testing 100's of thousands of transactions.
So in about 10 years this IDENTITY will have to be updated to a BIGINT or someone will have to convince the 3rd party vendor to refresh on their end.
Oh yeah, management could give a rat's ass about it until everything comes crashing down all of a sudden.
Then the HACK "ALTER TABLE tableName ALTER COLUMN ID bigint" will do just fine.
Space and index processing is CHEAP!

SQL: No Identity feature workaround using triggers

I'm a little rusty with my triggers and what not and am trying to figure out this problem for a class:
In a database TEST, tables do not have the option of the IDENTITY feature. In other words, when we insert a row into the table “Users”, we would like the primary key “UserID” to auto-increment. Please suggest a workaround to implement this feature without such a built-in functionality.
(Hint: You may still use functions, stored procedures, sequences, triggers, etc)
Use an Int column for the table Primary Key called ID.
You can then use an instead of Insert Trigger, to populate/calculate the value to be inserted for ID.
The trigger will determine what the maximum existing ID is for the table in question (using select MAX ID from TableA) and then increment it by 1 for each record to be inserted.
If there are no records in the table then the ID value is 1.
You use a sequence, and it's very common with Oracle, which does not (or did not once, it may have changed) have identity columns. Since this is homework I'll let you figure out the rest from here.