Complex queries in SQL Server 2005 database - sql-server-2005

I have a table in my database in which records conceptually can be children of other rcords. The table has a non-null name field. I need to ensure that each name in a set of children is unique, but not across the entire database. I'd like to enforce this using a constraint inside the Database. What's the best way to accomplish this?
I know that I am going to have to do a query at some point in the process like this:
#NameParameter NVARCHAR(512)
Select Name from MyTable
WHERE Name=#NameParameter
The question is where do I put this query?

I may not be understanding your question correctly, but my suggestion is to create another column that references the parent record of the child. You could then create a A multiple-column index based on those two columns to speed up any queries that reference these columns together in a where clause... Thus your uniqueness would come from {parent_name, child_name}. A constraint on these two columns would act as a key for that table, and not allow duplicates.
#childname NVARCHAR(255), #parentname NVARCHAR(255)
SELECT * FROM [child_records]
WHERE [parent_name] = #parentname
AND [child_name] = #childname

At first glance I believe this should go into a "Instead of trigger". This link provides a good example.

Related

Automatic Normalization of VARCHAR columns?

Imagine you have many tables which have a VARCHAR(50) column called Country, there is a small number of different country names which repeat millions of times, the wise thing to do here is to create a table called dbo.Country(CountryID, CountryName) and have all the tables hold CountryID with a foreign key reference.
Problem is we have to JOIN all our queries with dbo.Country every time we want do something with that column.
But all the joins seem to follow the same pattern, so my question is, can SQL Server do it automatically? For example I would specify a column called CountryName in some table which looks like a VARCHAR but is actually stored as a CountryID with foreign key, and SQL Server could implicitly add the JOIN whenever necessary.
Is there such a feature in SQL Server or any other SQL database?
You can't do this "automatically". However, you do have a couple of options.
One is to create a view on top of the table that automatically does the join:
create view v_table as
select t.*, c.CountryName
from table t join
country c
on t.countryId = c.countryId;
Alternatively, you could make Country an enumerated type. That would allow it to be accessed as a string but stored as an integer.

How can i make certain Oracle Table Rows marked as 'historical' invisible/un-available?

I have a huge existing Order Management Application.
Now, in the main ORDER Table, i am adding a new column: IS_HISTORICAL. If its value is: TRUE, means the Order is Historical now, and should not show up in application.
Now, i have to modify many SQL Queries in my existing application so that they select only those orders whose IS_HISTORICAL is 'FALSE' - i.e add following in WHERE clause:
AND IS_HISTORICAL='FALSE'
Question: *Is there a easier way - so that i do not have to modify so many application queries (to hide away historical orders)?
Essentially all ORDERS marked as IS_HISTORICAL='TRUE' should become invisible/un-available for read/updates!!*
Note: Right now the table sizes are not very huge, but ultimately i intend to partition the table by IS_HISTORICAL true/false.
If you're only going to use the historical data for analysis then I prefer Florin's solution as the amount of data you need to look at for each query remains smaller. It makes the analysis queries more difficult as you need to UNION ALL but everything else will run "quicker" (it may not be noticable).
If some applications/users require access to the historical data the better solution would be to rename your table and create a view on top of it with the query that you need.
The problem with re-writing all your queries is that you're going to forget one or get one incorrect, either now or in the future. A view removes that problem for you as the query is static, every time you query the view the additional conditions you require are automatically added.
Something like:
rename orders to order_history;
create or replace view orders as
select *
from order_history
where is_historical = 'FALSE';
Two further points.
I wouldn't bother with TRUE / FALSE, if the table gets large it's a lot of additional data to scan. Create your column as a VARCHAR2(1) and use T / F or Y / N, they are as immediately obvious but are smaller. Alternatively use a NUMBER(1,0) and 1 / 0.
Don't forget to put a constraint on your table so that the IS_HISTORICAL column can only have the values you've chosen.
If you're only ever going to have the two values then you may want to consider a CHECK CONSTRAINT:
alter table order_history
add constraint chk_order_history_historical
check ( is_historical in ('T','F') );
Otherwise, maybe you should do this anyway, use a FOREIGN KEY CONSTRAINT. Define an extra table, ORDER_HISTORY_TYPES
create table order_history_types (
id varchar2(1)
, description varchar2(4000)
, constraint pk_order_history_types primary key (id)
);
Fill it with your values and then add the foreign key:
alter table order_history
add constraint fk_order_history_historical
foreign key (is_historical)
references order_history_types (id)
You could look into using Virtual Private Database/row-level security. This can be used to automatically add the is_historical = 'FALSE' predicate when certain conditions are met (e.g. you're connected as the application user).
If the user only need nonhistorical records, an option is to create an ORDER_HIST table and move there the historical records. (delete and insert)
If some users/applications need both type of records then the partition aproach is the best.

can I insert a copy of a row from table T into table T without listing its columns and without primary key error?

I want to do something like this:
INSERT INTO T SELECT * FROM T WHERE Column1 = 'MagicValue' -- (multiple rows may be affected)
The problem is that T has a primary key column and so this causes an error as if trying to set the primary key. And frankly, I don't want to set the primary key either. I want to create entirely new rows with new primary keys but the rest of the fields being copied over from the original rows.
This is supposed to be generic code applicable to various tables. Well, so if there is no nice way of doing this, I will just write code to dynamically extract column names, construct the list etc. But maybe there is? Am I the first guy trying to create duplicate rows in a database or something?
I'm assuming by "Primary Key" you mean identity or guid data types that auto-assign or auto-increment.
Without some very fancy dynamic SQL, you can't do what you are after. If you want to insert everything but the identity field, you need to specify fields.
If you want to specify a value for that field, you need to specify all the fields in the SELECT and in the INSERT AND turn on IDENTITY_INSERT.
You don't gain anything from duplicating a row in a database (considering you didn't try to set the Primary Key). It would be wiser and will avoid problem to have another column called "amount" or something.
something like
UPDATE T SET Amount = Amount + 1 WHERE Column1 = 'MagicValue'
or if it can increase by more than 1 like amount of returned fields
Update T SET Amount = Amount * 2 WHERE Column1 = 'MagicValue'
I'm not sure what you're trying to do exactly but if the above doesn't work for what you're doing I think your design requires a new table and insert it there.
EDIT: Also as mentioned under your comments, a generic insert doesn't really make sense. Imagine, for this to work, you need the same number of fields, and they will hold the same values suggesting that they should also have the same names(even if it wouldn't require it to). It would basically be the same table structure twice.

MS SQL share identity seed amongst tables

In MS SQL is it possible to share an identity seed across tables? For example I may have 2 tables:
Table: PeopleA
id
name
Table: PeopleB
id
name
I'd like for PeopleA.id and PeopleB.id to always have unique values between themselves. I.e. I want them to share the same Identity seed.
Note: I do not want to hear about table partitioning please, only about if it's possible to share a seed across tables.
Original answer
No you can't and if you want to do this, your design is almost
certainly flawed.
When I wrote this in 2010 that was true. However, at this point in time SQL Server now has Sequences that can do what the OP wants to do. While this may not help the OP (who surely has long since solved his problem), it may help some one else looking to do the same thing. I do still think that wanting to do this is usually a sign of a design flaw but it is possible out of the box now.
No, but I guess you could create an IDENTITY(1, 2) on the one table and an IDENTITY(2, 2) on the other. It's not a very robust design though.
Could you instead refer to your entities as 'A1', 'A2', ... if they come from TableA and 'B1', 'B2', etc... if they come from TableB? Then it's impossible to get duplicates. Obviously you don't actually need to store the A and the B in the database as it is implied.
Not sure what your design is, but sometimes it is useful to use an inheritance-type model, where you have a base table and then sub-tables with substantially different attributes, e.g.:
Person
------
PersonID <-- PK, autoincrement
FirstName
LastName
Address1
...
Employee
--------
PersonID <-- PK (not autoincrement), FK to Person
JobRoleID
StartDate
Photo
...
Associate
---------
PersonID <-- PK (not autoincrement), FK to Person
AssociateBranchID
EngagementTypeID
...
In this case you would insert the base values to Person, and then use the resulting PersonID to insert into either Employee or Associate table.
If you really need this, create a third table PeopleMaster, where the identity(1,1) exists, make the two other tables just have int FKs to this identity value. Insert into the PeopleMaster and then into PeopleA or PeopleB.
I would really consider this a bad design though. Create one table with a PeopleType flag ("A" or "B") and include all common columns, and create child tables if necessary (for any different columns between the PeopleA and PeopleB)
No.
But I have worked on projects where a similar concept was used. In my case what we did was have a table called [MasterIdentity] which had one column [Id] (an identity seed). No other table in the database had any columns with an identity seed and when Identities were required a function/stored proc was called to insert a value into the [MasterIdentity] table and return the seed.
No, there is nothing built into SQL Server to do this.
Obviously there are workarounds such as both using an FK relationship to a table which does have a single IDENTITY and having some fancy constraints or triggers.

Records linked to any table?

Hi Im struggling a bit with this and could use some ideas...
Say my database has the following tables ;
Customers
Supplers
SalesInvoices
PurchaseInvoices
Currencies
etc etc
I would like to be able to add a "Notes" record to ANY type of record
The Notes table would like this
NoteID Int (PK)
NoteFK Int
NoteFKType Varchar(3)
NoteText varchar(100)
NoteDate Datetime
Where NoteFK is the PK of a customer or supplier etc and NoteFKType says what type of record the note is against
Now i realise that I cannot add a FK which references multiple tables without NoteFK needing to be present in all tables.
So how would you design the above ?
The note FK needs to be in any of the above tables
Cheers,
Daniel
You have to accept the limitation that you cannot teach the database about this foreign key constraint. So you will have to do without the integrity checking (and cascading deletes).
Your design is fine.
It is easily extensible to extra tables, you can have multiple notes per entity, and the target tables do not even need to be aware of the notes feature.
An advantage that this design has over using a separate notes table per entity table is that you can easily run queries across all notes, for example "most recent notes", or "all notes created by a given user".
As for the argument of that table growing too big, splitting it into say five table will shrink the table to about a fifth of its size, but this will not make any difference for index-based access. Databases are built to handle big tables (as long as they are properly indexed).
I think your design is ok, if you can accept the fact, that the db system will not check whether a note is referencing an existing entity in other table or not. It's the only design I can think of that doesn't require duplication and is scalable to more tables.
The way you designed it, when you add another entity type that you'd like to have notes for, you won't have to change your model. Also, you don't have to include any additional columns in your existing model, or additional tables.
To ensure data integrity, you can create set of triggers or some software solution that will clean notes table once in a while.
I would think twice before doing what you suggest. It might seem simple and elegant in the short term, but if you are truly interested in data integrity and performance, then having separate notes tables for each parent table is the way to go. Over the years, I've approached this problem using the solutions found in the other answers (triggers, GUIDs, etc.). I've come to the conclusion that the added complexity and loss of performance isn't worth it. By having separate note tables for each parent table, with an appropriate foreign key constraints, lookups and joins will be simple and fast. When combining the related items into one table, join syntax becomes ugly and your notes table will grow to be huge and slow.
I agree with Michael McLosky, to a degree.
The question in my mind is: What is the technical cost of having multiple notes tables?
In my mind, it Is preferable to consolidate the same functionality into a single table. It aso makes reporting and other further development simpler. Not to mention keeping the list of tables smaller and easier to manage.
It's a balancing act, you need to try to predetermine both the benefits And the costs of doing something like this. My -personal- preference is database referential integrity. Application management of integrity should, in my opinion, be limitted ot business logic. The database should ensure the data is always consistent and valid...
To actually answer your question...
The option I would use is a check constraint using a User Defined Function to check the values. This works in M$ SQL Server...
CREATE TABLE Test_Table_1 (id INT IDENTITY(1,1), val INT)
GO
CREATE TABLE Test_Table_2 (id INT IDENTITY(1,1), val INT)
GO
CREATE TABLE Test_Table_3 (fk_id INT, table_name VARCHAR(64))
GO
CREATE FUNCTION id_exists (#id INT, #table_name VARCHAR(64))
RETURNS INT
AS
BEGIN
IF (#table_name = 'Test_Table_1')
IF EXISTS(SELECT * FROM Test_Table_1 WHERE id = #id)
RETURN 1
ELSE
IF (#table_name = 'Test_Table_2')
IF EXISTS(SELECT * FROM Test_Table_2 WHERE id = #id)
RETURN 1
RETURN 0
END
GO
ALTER TABLE Test_Table_3 WITH CHECK ADD CONSTRAINT
CK_Test_Table_3 CHECK ((dbo.id_exists(fk_id,table_name)=(1)))
GO
ALTER TABLE [dbo].[Test_Table_3] CHECK CONSTRAINT [CK_Test_Table_3]
GO
INSERT INTO Test_Table_1 SELECT 1
GO
INSERT INTO Test_Table_1 SELECT 2
GO
INSERT INTO Test_Table_1 SELECT 3
GO
INSERT INTO Test_Table_2 SELECT 1
GO
INSERT INTO Test_Table_2 SELECT 2
GO
INSERT INTO Test_Table_3 SELECT 3, 'Test_Table_1'
GO
INSERT INTO Test_Table_3 SELECT 3, 'Test_Table_2'
GO
In that example, the final insert statement would fail.
You can get the FK referential integrity, at the costing of having one column in the notes table for each other table.
create table Notes (
id int PRIMARY KEY,
note varchar (whatever),
customer_id int NULL REFERENCES Customer (id),
product_id int NULL REFERENCES Product (id)
)
Then you'll need a constraint to make sure that you have only one of the columns set.
Or maybe not, maybe you might want a note to be able to be associated with both a customer and a product. Up to you.
This design would require adding a new column to Notes if you want to add another referencing table.
You could add a GUID field to the Customers, Suppliers, etc. tables. Then in the Notes table, change the foreign key to reference that GUID.
This does not help for data integrity. But it makes M-to-N relationships easily possible to any number of tables and it saves you from having to define a NoteFKType column in the Notes table.
You can easily implement "multi"-foreign key with triggers. Triggers will give you very flexible mechanism and you can do any integrity checks you wish.
Why dont you do it the other way around and have a foreign key in other tables (Customer, Supplier etc etc) to NotesID. This way you have one to one mapping.