Enforcing referential integrity from one table to any table - sql

I have a variety of tables that represent business objects, e.g. Products, People, Locations and I'm wanting to add a Tags table (as in taxonomy).
CREATE TABLE Tags (
tagId bigint IDENTITY,
name nvarchar(50)
)
How can I allow multiple tags to be applied to multiple types of entities in a way that allows the DBMS to enforce referential integrity without needing a linking table like this:
CREATE TABLE TagUse (
tagId bigint,
productId bigint NULL,
personId bigint NULL,
locationId bigint NULL,
...
whateverId bigint NULL
)
Or worse:
CREATE TABLE PersonTags (
tagId bigint,
personId bigint
)
CREATE TABLE LocationTags (
tagId bigint,
locationId bigint
)
...
CREATE TABLE WhateverTags (
tagId bigint,
whateverId bigint
)
I just thought of a third option: rather than having separate *Tags tables for each entity, each entity can be thought of inheriting from "Taggable" which is then referenced-to by the child tables:
CREATE TABLE Taggable (
taggableId bigint,
tagId bigint
)
CREATE TABLE Persons (
personId bigint,
...
taggableId bigint
)

You could use a sequence object:
CREATE TABLE [TaggableObjects] (
objectId BIGINT IDENTITY(1,1)
)
CREATE TABLE [Persons] (
objectId BIGINT, -- referencing Objects.objectId
name VARCHAR(50)
)
CREATE TABLE [Locations] (
objectId BIGINT, -- referencing Objects.objectId
country VARCHAR(50)
)
In programming languages like Java and C# you'd call Persons and Locations an extension of TaggableObject.
Following this you could implement the Tags as:
CREATE TABLE [Tags] (
tagId BIGINT IDENTITY(1,1),
objectId BIGINT
)
Though the problem with an implementation like this one would be that you have to figure out the best way to identify of what type Tags.objectId is.
Note that depending on which engine you'll be using, many support some sort of functionality to more easily implement a sequence object, you might want to right up on it.

Related

Can I insert into multiple related tables in a single statement?

I have two related tables something like this:
CREATE TABLE test.items
(
id INT identity(1,1) PRIMARY KEY,
type VARCHAR(max),
price NUMERIC(6,2)
);
CREATE TABLE test.books
(
id INT PRIMARY KEY REFERENCES test.items(id),
title VARCHAR(max),
author VARCHAR(max)
);
Is it possible to insert into both tables using a single SQL statement?
In PostgreSQL, I can use something like this:
-- PostgreSQL:
WITH item AS (INSERT INTO test.items(type,price) VALUES('book',12.5) RETURNING id)
INSERT INTO test.books(id,title) SELECT id,'Good Omens' FROM item;
but apparently SQL Server limits CTEs to SELECT statements, so that won’t work.
In principle, I could use the OUTPUT clause this way:
-- SQL Server:
INSERT INTO test.items(type, price)
OUTPUT inserted.id, 'Good Omens' INTO test.books(id,title)
VALUES ('book', 12.5);
but this doesn’t work if there’s a foreign key involved, as above.
I know about using variables and procedures, but I wondered whether there is a simple single-statement approach.
You can using dynamic sql as follows. Although its awkward to construct query like this.
CREATE TABLE dbo.items (
id INT identity(1,1) PRIMARY KEY,
type VARCHAR(max),
price NUMERIC(6,2)
);
CREATE TABLE dbo.books (
id INT PRIMARY KEY REFERENCES dbo.items(id),
title VARCHAR(max),
author VARCHAR(max)
);
insert into dbo.books(id,title)
exec ('insert into dbo.items(type,price) output inserted.id,''Good Omen'' VALUES(''book'',12.5)')

Suggested Indexing for table with 50 million rows is queried using its CREATED_DATE column and USER_TYPE column

Table Users:
ID PK INT
USER_TYPE VARCHAR(50) NOT NULL
CREATED_DATE DATETIME2(7) NOT NULL
I have this table with 50 million rows, and it is queries using the following where clause:
WHERE
u.USER_TYPE= 'manager'
AND u.CREATED_DATE >= #StartDate
AND u.CREATED_DATE < #EndDate
What would be a good starting point for an index on this table to optimize for the above query where clause?
For that query, the index you want is a composite index with two columns: (user_type, created_date). The order matters, you want user_type first because of the equality comparison.
You'll be well served by creating a table with user types having an arbitrary INT ID and referring to the manager type by ID, instead of having the manager type directly in the users table. This will narrow the table data as well as any index referring to the user type.
CREATE TABLE user_type (
id INT NOT NULL IDENTITY(1,1),
description NVARCHAR(128) NOT NULL,
CONSTRAINT pk_user_type PRIMARY KEY CLUSTERED(id)
);
CREATE TABLE users (
id INT NOT NULL IDENTITY(1,1),
user_type_id INT NOT NULL,
created_date DATETIME2(7) NOT NULL,
CONSTRAINT pk_users PRIMARY KEY CLUSTERED(id),
CONSTRAINT fk_users_user_type FOREIGN KEY(user_type_id) REFERENCES user_type(id)
);
CREATE NONCLUSTERED INDEX
ix_users_type_created
ON
users (
user_type_id,
created_date
);
You would be querying using the user_type ID rather than directly with the text of course.
For any query. Run the query in SSMS with "Include Actual Execution Plan" on. SSMS will advice an index if it feels proper index doesn't exist.

Creating table when each object may have a list of values

First I've created a table with information on stores and transactions with the following query:
CREATE TABLE main.store_transactions
(
store_id varchar(100) NOT NULL,
store_name varchar(100),
store_transaction_id varchar(100),
transaction_name varchar(100),
transaction_date timestamp,
transaction_info varchar(200),
primary_key(store_id)
)
But then I realized that the same store may have various transactions related to it, not just one. How should I implement table creation in this case?
One thing that comes to mind is to create a separate table with transactions, each transaction having store_id as a foreign key. And then just join when needed.
How is it possible to implement it in a single table?
Well, the most elegant way would be indeed to create a satelite table for your stores and reference it to the store_transactions table, e.g:
CREATE TABLE stores
(
store_id varchar(100) NOT NULL PRIMARY KEY,
store_name varchar(100)
);
CREATE TABLE store_transactions
(
store_id varchar(100) NOT NULL REFERENCES stores(store_id),
store_transaction_id varchar(100),
transaction_name varchar(100),
transaction_date timestamp,
transaction_info varchar(200)
);
With this structure you will have many transactions to a single store.
There are other less appealing options, such as customizing a data type for stores and creating an array of it in the table store_transactions. But regarding the costly maintainability of such approach, I would definitely discourage it.

How to create a db model with table rows that have relationships to ever growing attributes?

For example, if I have a table that takes some type of 'item' whether it be products, animals, what have you, and I need to also store their attributes that can grow over time. What is the best way to model?
One way would be:
CREATE TABLE item
(
item_id int,
name varchar(255),
description varchar(255),
);
CREATE TABLE item_attr
(
item_attr_id int,
item_id int,
name varchar(255),
description varchar(255),
value varchar(255)
)
Now if I add an item to the items table, and this item gets 100 different attributes with name, description, any possible value (number, text, etc), then I would have 100 rows just for that one item in the item_attr table. Is that the best way to do it?
It depends whether the attributes are share between items. In that case you would create them in a separate table and create linktable between them
CREATE TABLE item
(
item_id int,
name varchar(255),
description varchar(255),
);
CREATE TABLE attr
(
attr_id int,
name varchar(255),
description varchar(255),
)
CREATE TABLE item_attr
(
item_attr_id int,
item_id int,
attr_id int,
value varchar(255)
)

Type collection in a table SQL Server Compact

I work on a program VB.Net using a SQL Server Compact database.
I want to create a table PRODUCTION where I can have many employees so I want to create a column as collection of names of employees:
This is the table Employee :
CREATE TABLE [Employee]
(
[ID] INT NOT NULL IDENTITY (1,1),
[nom] NVARCHAR(100),
[salaire] REAL,
[date_debut] DATETIME,
[tache] NVARCHAR(100)
);
ALTER TABLE [Employe] ADD CONSTRAINT [PK_Employe] PRIMARY KEY ([ID]);
The table PRODUCTION I want to create :
create table production(
ID int not null identity(1,1),
date_prod Datetime,
emp collection (emp1,emp2, ...));
Explanation: in the table Production, I will have many employees, so I want to insert them in one line of the entry.
Any idea?