I have a table that has user a user_id and a new record for each return reason for that user. As show here:
| user_id | return_reason |
|--------- |-------------- |
| 1 | broken |
| 2 | changed mind |
| 2 | overpriced |
| 3 | changed mind |
| 4 | changed mind |
What I would like to do is generate a foreign key for each combination of values that are applicable in a new table and apply that key to the user_id in a new table. Effectively creating a many to many relationship. The result would look like so:
Dimension Table ->
| reason_id | return_reason |
|----------- |--------------- |
| 1 | broken |
| 2 | changed mind |
| 2 | overpriced |
| 3 | changed mind |
Fact Table ->
| user_id | reason_id |
|--------- |----------- |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 3 |
My thought process is to iterate through the table with a cursor, but this seems like a standard problem and therefore has a more efficient way of doing this. Is there a specific name for this type of problem? I also thought about pivoting and unpivoting. But that didn't seem too clean either. Any help or reference to articles in how to process this is appreciated.
The problem concerns data normalization and relational integrity. Your concept doesn't really make sense - Dimension table shows two different reasons with same ID and Fact table loses a record. Conventional schema for this many-to-many relationship would be three tables like:
Users table (info about users and UserID is unique)
Reasons table (info about reasons and ReasonID is unique)
UserReasons junction table (associates users with reasons - your
existing table). Assuming user could associate with same reason
multiple times, probably also need ReturnDate and OrderID_FK fields
in UserReasons.
So, need to replace reason description in first table (UserReasons) with a ReasonID. Add a number long integer field ReasonID_FK in that table to hold ReasonID key.
To build Reasons table based on current data, use DISTINCT:
SELECT DISTINCT return_reason INTO Reasons FROM UserReasons
In new table, rename return_reason field to ReasonDescription and add an autonumber field ReasonID.
Now run UPDATE action to populate ReasonID_FK field in UserReasons.
UPDATE UserReasons INNER JOIN UserReasons.return_reason ON Reasons.ReasonDescription SET UserReasons.ReasonID_FK = Reasons.ReasonID
When all looks good, delete return_reason field.
I have two tables in my SQL Server database, Foo and Bar. Table Foo is like so:
+-------+
| Foo |
+-------+
| Id |
| Type |
| Value |
+-------+
The table has values like:
+----+--------+-----------+
| Id | Type | Value |
+----+--------+-----------+
| 1 | Status | New |
| 2 | Status | Old |
| 3 | Type | Car |
| 4 | State | Inventory |
| 5 | State | Sold |
+----+--------+-----------+
The table Bar is like so:
+----------+
| Bar |
+----------+
| Id |
| TypeId |
| StatusId |
| StateId |
+----------+
Where TypeId, StatusId and StateId are all foreign key'ed to the Foo table.
But I want to put a condition on each foreign key where they can only key to the Foo
ids related to it's type. For example, the TypeId column can ONLY foreign key to id
3 on the Foo table. Or the StatusId column can ONLY foreign key to ids 1 or 2.
I know there is a check function in SQL Server but I'm unsure on how to use it correctly. I
tried to do something like this:
CREATE TABLE TEST.dbo.Bar
(
Id int PRIMARY KEY NOT NULL IDENTITY,
TypeId int NOT NULL CHECK (Type='Type'),
CONSTRAINT FK_Bar_Foo_Type FOREIGN KEY (TypeId) REFERENCES Foo (Id, Type)
)
CREATE UNIQUE INDEX Bar_Id_uindex ON TEST.dbo.Bar (Id)
But this didn't work. What am I doing wrong?
The check constraints you are referring to are only used to limit the type of information stored in a key or non key column. So, if you don't want a key column to have a negative value (lets say its a price column, and there is never a negative price) you will use Check constraint.
To better understand the concept of primary and foreign keys:
Primary key uniquely identifies each record in a table.
Foreign key is a value in some table which is a unique identifier (and can also be a primary key) in another table. This means that Foreign key can repeat many times in the table in which it is a foreign key in, and it will definitely be unique in the table that it is created from ( in the table that gives meaning to it).
Now coming to your question, you probably need to use the concept of composite keys. A composite key is basically a group of two or more values that uniquely identify a record, because you cannot enforce limitations on foreign keys in the way you are intending to do, because that defeats the very purpose of a key. Handle some issues with type of data stored in your keys at the application layer instead of database layer.
Looking at the problem in this manner will conceptually resolve some design flaws with your tables as as well.
I have three tables: Products, Attributes and AttributesDefinitions.
Products:
+----+------+
| Id | Name |
+----+------+
| 1 | Shoe1|
+----+------+
| 2 | Shoe2|
+----+------+
| 3 | Shoe3|
+----+------+
AttributesDefinition:
+----+---------+
| Id | Name |
+----+---------+
| 1 | Color |
| 2 | Type |
| 3 | Destiny |
+----+---------+
Attributes:
+----+--------------+--------------+-----------+
| Id | Value | DefinitionId | ProductId |
+----+--------------+--------------+-----------+
| 1 | Brown | 1 | 2 |
| 2 | Yellow | 1 | 1 |
| 3 | Sport | 3 | 1 |
| 4 | Jelly shoes | 2 | 1 |
| 5 | Normal shoes | 2 | 2 |
+----+--------------+--------------+-----------+
In AttributesDefinitions I have wanted attributes definitions.
In Attributes I have attributes and their values.
Each Product has many attributes, but only 1 of each type (attribute definition).
My task is to make a view containing list of products and all their attributes values.
It should look like this:
ProductsWithAttributesView:
+---------+--------+--------------+---------+
| Product | Color | Type | Destiny |
+---------+--------+--------------+---------+
| Shoe1 | Yellow | Jelly shoes | Sport |
| Shoe2 | Brown | Normal shoes | NULL |
| Shoe3 | NULL | NULL | NULL |
+---------+--------+--------------+---------+
The purpose of this is getting list of products on B2B platform and being able to filter them by values of attributes.
Any help how can I achieve that ?
I use code from CaitLAN Jenner to present my solution, but I`m not sure if you can make view which will dynamically adapt when new category is added.
CREATE TABLE Products
(
Id INT PRIMARY KEY,
Name VARCHAR(255)
);
INSERT INTO Products (Id,Name)
VALUES (1,'Car'),(2,'Motorcycle'),(3,'Bicycle')
CREATE TABLE AttributesDefinition
(
Id INT PRIMARY KEY,
Name VARCHAR(255)
);
INSERT INTO AttributesDefinition (Id,Name)
VALUES (1,'Number of wheels'),(2,'People'),(3,'Engine')
CREATE TABLE Attributes
(
Id INT,
Name VARCHAR(255),
Value VARCHAR(255),
DefinitionId INT FOREIGN KEY REFERENCES AttributesDefinition (Id),
ProductId INT FOREIGN KEY REFERENCES Products (Id)
);
INSERT INTO Attributes (Id, Name, Value, DefinitionId, ProductId)
VALUES (1,'Number of wheels','4',1,1),
(2,'Number of wheels','2',1,2),
(3,'Number of wheels','2',1,3),
(4,'People','4',2,1),
(5,'People','2',2,2),
(6,'People','1',2,3),
(7,'Engine','V6',3,1),
(8,'Engine','V2',3,2)
CREATE VIEW ProductsWithAttributesView AS
SELECT
products.Name as 'Products',
atr1.Value As 'Number of wheels',
atr2.Value As 'People',
atr3.Value As 'Engine'
FROM Products AS products
LEFT JOIN Attributes AS atr1 ON atr1.ProductId = products.Id AND atr1.DefinitionId = 1
LEFT JOIN Attributes AS atr2 ON atr2.ProductId = products.Id AND atr2.DefinitionId = 2
LEFT JOIN Attributes AS atr3 ON atr3.ProductId = products.Id AND atr3.DefinitionId = 3
Result
Products | Number of wheels | People | Engine
Car | 4 | 4 | V6
Motorcycle| 2 | 2 | V2
Bicycle | 2 | 1 | NULL
Edit. This one with pivot:
CREATE VIEW ProductsWithAttributesView2 AS
WITH pivot_data AS
(
SELECT products.Name as Products, -- Grouping Column
def.Name as AtrName, -- Spreading Column
Value -- Aggregate Column
FROM Attributes atr
INNER JOIN Products products ON atr.ProductId = products.Id
INNER JOIN AttributesDefinition def ON def.Id = atr.DefinitionId
)
SELECT Products, [Number of wheels],[People],[Engine]
FROM pivot_data
PIVOT (max(value) FOR AtrName IN ([Number of wheels],[People],[Engine])) AS p;
As I stated in my comment, I strongly disagree with the schema you've described because it appears to me that the Products table should have either a one-to-many or many-to-many relationship with Attributes. In other words, one Product can have many Attributes, and many Products can share the same Attribute. Additionally, because of the design you've mentioned and what your end goal is (a Product with an arbitrary-length list of attributes as columns) what your asking for may not be possible. You might could achieve those results with a carefully crafted PIVOT statement within the View definition. First let me know if the code below works.
With all of that said, if I make the assumption that you are using T-SQL, then this code will create the tables you've described and create a View based performing a JOIN on the tables. Hopefully this pushes you in the correct direction for moving forward.
CREATE TABLE Products
(
Id INT PRIMARY KEY,
Name VARCHAR(255)
);
CREATE TABLE AttributesDefinition
(
Id INT PRIMARY KEY,
Name VARCHAR(255)
);
CREATE TABLE Attributes
(
Id INT,
Name VARCHAR(255),
Value VARCHAR(255),
DefinitionId INT FOREIGN KEY REFERENCES AttributesDefinition (Id),
ProductId INT FOREIGN KEY REFERENCES Products (Id)
);
CREATE VIEW ProductsWithAttributesView AS
SELECT p.Name AS Products
,ad.Name AS AttributeDefinition
,a.Name AS AttributeName
,a.Value AS AttributeValue
FROM Products p
INNER JOIN Attributes a ON p.Id = a.ProductId
INNER JOIN AttributesDefinition ad ON ad.Id = a.DefinitionId;
SELECT * FROM ProductsWithAttributesView;
I am wondering if I can add a foreign key to an already existing table with records in it.
The tables structure and the data in it looks like that at the moment:
Books table:
ID | BookName | BookCode | BookEdition | AuthorID
1 | Name1 | Code1 | 1 | 1
2 | Name1 | Code1 | 2 | 2
Authors table:
ID | AuthorName
1 | Name1
2 | Name2
Basically, I want to add link the two tables based on the AuthorID, the problem is that the column already has data in it, as shown in the example above, but these ids are the correct ones and they also exist in the Authors table.
Is it possible to do that without re-creating the tables?
You can do this using alter table:
alter table books add constraint fk_books_authorid
foreign key (AUthorId) references Authors(ID);
I have two tables in one database (access file).
Table 1:
| Product Id | Year | Sales |
----------------------------------
| 1144 | 2013 | 100 |
| 20131120 | 2013 | 200 |
| 1144 | 2012 | 333 |
Table 2:
| Product Id | Category |
----------------------------------
| 1144 | Car |
| 20131120 | Motorbike |
The first table (table 1) is the table when user can add new data whenever wants, and the second table is updated once in a while, when new product id appears. I would like to join this table, some kind of relation on the base of Product Id, but I'm not able to use relation due to the lack of unique, primary key, am I right? When I execute SQL query ( SELECT * FROM Table 1) I would like to get the result like this one:
| Product Id | Year | Sales | Category |
-----------------------------------------------
| 1144 | 2013 | 100 | Car |
| 20131120 | 2013 | 200 | Motrobike |
| 1144 | 2012 | 333 | Car |
I know I can join this two tables, but I would like not to do it every time when user add new data to table 1. Do you know any solution how could I do that?
I'm not able to use relation due to the lack of unique, primery key,
am I right?
No, this is not right, you can normally JOIN the two tables. It’s also legal to JOIN two tables or result sets on any columns that have what so called eligible data types.
SELECT
t1.ProductId, t1.year, t1.sales, t2.category
FROM table1 AS t1
INNER JOIN table2 AS t2 ON t1.productId = t2.productId;
but I would like not to do it every time when user add new data to
table 1
You don't need a ProductId in the second table, you need a CategoryId instead, and add a new column CategoryId to the first table too and declare it as a foreign key. Your tables should look like so:
Table1 (Products):
ProductId,
CategoryId,
Year,
Sales.
Table2 (Categories):
CategoryId,
CategoryName.
Then the category is added once to the categories table table2 and the product is added once to the products table table1. And for each product enter a CategoryId for it. This what so called normalization
You should also define ProductId in table1 and CategoryID in table2 as primary keys, and CategoryID in the first table as foreign key.