I have a number of tables in which I need to reference scene IDs, which could be a SET. The problem is that I need to be able to update a set in a table that contains my login information for the app. This set needs to expand(or potentially shrink) based on the number of scenes that exist on the DB. Is it possible to do in phpmyadmin?
From what I've seen in the web interface, I must pre-define the SET values. But I cannot find any info on how to edit the SET's possible values after the column has been created.
What you have is a many-to-many relationship between logins and scenes.
The correct way to implement this is with three tables, for example:
CREATE TABLE logins (login_id INT PRIMARY KEY ...);
CREATE TABLE scenes (scene_id INT PRIMARY KEY ...);
CREATE TABLE login_has_scene (
login_id INT NOT NULL,
scene_id INT NOT NULL,
PRIMARY KEY (login_id, scene_id),
FOREIGN KEY (login_id) REFERENCES logins (login_id),
FOREIGN KEY (scene_id) REFERENCES logins (scene_id)
);
This way you can add new scenes anytime, and you can reference any scene from any login by adding one row per login-scene pair.
This is better than using a SET because SET requires you to redefine the list of values using ALTER TABLE every time you add a scene, and this will become quite a chore.
Also a SET column only allows up to 64 distinct values. If you ever want these tables to support further scenes, you'd have to add more SET columns, or start recycling scene ids or something.
The many-to-many table is a much better solution.
Frankly, I have been using MySQL for nearly 20 years, and I've never found a good use for the SET data type.
Related
I want to structure a MySQL table with many of the the usual columns such as index as an integer, name as a varchar, etc. The thing about this table is I want to include a column that has an unknown number of entries. I think the best way to do this (if possible) is to make one of the columns an array that can be changed as any entry in a database can. Supposing when the record is created it has 0 entries. Then later, I want to add 1 or more. Maybe sometime later still, I might want to remove 1 or more of these entries.
I know I could create the table with individual columns for each of these additions, but I may want as many as a hundred or more for one record. This seems very inefficient and very difficult to maintain. So the bottom-line question is can a column be defined as a dynamic array? If so, how? How can things be selectively added to or removed from it?
I'll take a stab in the dark and guess maybe make a table contain another table. I've never heard of this because my experience with MySQL has been mostly casual. I make databases and dynamic websites because I want to.
The way to do this in a relational database is to create another table. One column of that table will have a foreign key pointing to the primary key of that table that should have had the array (or multiple columns, if the primary key consists of more than one row). Another column has the values that'd be found in the array. If order matters, a third column would store some other values indicating the ordinality.
Something along the lines of:
CREATE TABLE elbat_array
(id integer,
elbat integer -- or whatever type the primary key column has
NOT NULL,
value text, -- or whatever type the values should have
ordinality integer
NOT NULL, -- optional
PRIMARY KEY (id),
FOREIGN KEY (elbat)
REFERENCES elbat -- the other table
(id) -- and its primary key column
ON DELETE CASCADE,
UNIQUE (ordinality));
To add to the "array", insert rows into that table. To remove, delete rows. There can be as many as zero rows (i.e. "array" elements) or as much as there's disk space (unless you hit any limit of the DBMS before, but if such a limit applies it would be very large, so usually that should not be a problem).
Also worth a read in that context: "Is storing a delimited list in a database column really that bad?" While it's not about an array type in particular, on the meta level it discusses why the values in a column should be atomic. An array would violate that as well as a delimited list does.
If I create two tables and I want to set one column as foreign key to another table column why the hell am I allowed to set foreign key column datatype?
It just doesn't make any sense or am I missing something? Is there any scenario where column with foreign keys has different datatype on purpose?
Little more deeper about my concerns, I tried to use pgadmin to build some simple Postgres DB. I made first table with primary key serial datatype. Then I tried to make foreign key but what datatype? I have seen somewhere serial is bigint unsigned. But this option doesn't even exists in pgadmin. Of course I could use sql but then why am I using gui? So I tried Navicat instead, same problem. I feel like with every choice I do another mistake in my DB design...
EDIT:
Perhaps I asked the question wrong way.
I was allowed to do build structure:
CREATE TABLE user
(
id bigint NOT NULL,
CONSTRAINT user_pkey PRIMARY KEY (id)
)
WITH (
OIDS=FALSE
);
CREATE TABLE book
(
user integer,
CONSTRAINT dependent_user_fkey FOREIGN KEY (user)
REFERENCES user (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
I insert some data to table user:
INSERT INTO user(id)
VALUES (5000000000);
But I can't cast following insert:
INSERT INTO book(user)
VALUES (5000000000);
with ERROR: integer out of range which is understandable, but obvious design error.
And my question is: Why when we set CONSTRAINT, data types are not being validated. If I'm wrong, answer should contain scenario where it is useful to have different data types.
Actually it does make sense here is why:
In a table, you can in fact set any column as its primary key. So it could be integer, double, string, etc. Even though nowadays, we mostly use either integers or, more recently, strings as primary key in a table.
Since the foreign key is pointing to another table's primary key, this is why you need to specify the foreign key's datatype. And it obviously needs to be the same datatype.
EDIT:
SQL implementations are lax on this case as we can see: they do allow compatible types (INT and BIG INT, Float or DECIMAL and DOUBLE) but at your own risk. Just as we can see in your example, below.
However, SQL norms do specify that both datatypes must be the same.
If datatype is character, they must have the same length, otherwise, if it is integer, they must have the same size and must both be signed or both unsigned.
You can see by yourself over here, a chapter from a MySQL book published in 2003.
Hope this answers your question.
To answer your question of why you'd ever want different type for a foreign vs. primary key...here is one scenario:
I'm in a situation where an extremely large postgres table is running out of integer values for its id sequence. Lots of other, equally large tables have a foreign key to that parent table.
We are upsizing the ID from integer to bigint, both in the parent table and all the child tables. This requires a full table rewrite. Due to the size of the tables and our uptime commitments and maintenance window size, we cannot rewrite all these tables in one window. We have about three months before it blows up.
So between maintenance windows, we will have primary keys and foreign keys with the same numeric value, but different size columns. This works just fine in our experience.
Even outside an active migration strategy like this, I could see creating a new child table with a bigint foreign key, with the anticipation that "someday" the parent table will get its primary key upsized from integer to bigint.
I don't know if there is any performance penalty with mismatched column sizes. That question is actually what brought me to this page, as I've been unable to find guidance on it online.
(Tangent: Never create any table with an integer id. Go with bigint, no matter what you think your data will look like in ten years. You're welcome.)
Not really a DBA, but was tasked with designing a couple new tables for a new feature in a web app. DB is MySQL, using NHibernate as ORM (though that's probably irrelevant to the question).
I'm going to be modelling various, "scenarios" which represent different variations of several designs in the app. Aside from the first scenario & "unstarted" scenarios, each scenario will have a parent scenario they're building from. As a result, will end up with a sort of "no-loop / no-merge" tree structure as scenarios are branched from one another.
CREATE TABLE `scenarios` (
`ScenarioID` INT NOT NULL AUTO_INCREMENT,
`DesignID` INT DEFAULT NULL,
`ScenarioTreeID` INT NOT NULL,
`ParentScenarioID` INT DEFAULT NULL,
`Title` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
...
In addition to the scenarios themselves, there's information that's best related to the entire "Tree" of scenarios (e.g. what structure are the scenarios related to, etc). I've tried to factor this data out into another table called scenariotree and reference it from scenarios via ScenarioTreeID. The issue I ran into was, from a querying perspective, that it'd be important to know what the "root scenario" is when I query the tree (I can't just go WHERE ParentScenarioID is NULL as that includes "unstarted" scenarios). So I tried to set up the table as such:
CREATE TABLE `scenariotree` (
`ScenarioTreeID` INT NOT NULL AUTO_INCREMENT,
`StructureID` INT NOT NULL,
`RootScenario` INT DEFAULT NULL,
...
But then I couldn't create either table due to the circular foreign key references. I realise I can create the tables first & then add the foreign keys in (or just turn FK checks off & then on again when I'm finished), but should I be doing this? Poking around online I'm finding conflicting opinions. Basically what I want to ask is:
"Is this acceptable schema design, or am I going to run into issues down the road? If so, what issues am I likely to have & how might I restructure these tables to avoid them?"
It's fine to have circular references. They are less common than not have cycles, but they are legitimate to model some data structures.
They do require some special handling, as you discovered. That's okay and it's necessary.
You already identified two ways of handling them:
SET FOREIGN_KEY_CHECKS=0; temporarily while you insert the mutually-depended data. One problem with this is that some people forget to re-enable the checks, and then some weeks later discover that their data is full of references that point to non-existing data.
Create the table first, then use ALTER TABLE to add the foreign keys after you populate the data. The problem here is that if you need to add new rows to existing tables, you'd have to drop the foreign keys and re-add them every time, and this affects all clients, not just your session.
A couple of other options:
Make one or the other foreign key nullable. When you need to insert mutually-dependent rows in the two tables, insert the one with nullable FK first, and use a NULL. Then insert to the other table. Then UPDATE the first table to assign the non-NULL value it should reference.
Finally, don't use FOREIGN KEY constraints. You will have columns that reference other columns, but it's sort of on the "honor system" instead of having a RDBMS-enforced constraint. This comes with its own risks of course, because any data that is supposed to be a foreign key has no assurance that it correct. But it gives you total freedom to insert in whatever order you need to. You can use a transaction to make sure inserts to both tables happen together.
I have a portal model. Every user gets their own portal. Now the user can customize various strings across multiple pages on that portal to show their customers. Overall there are around 50 strings and an average user changes from defaults to around 7 of them.
One way to go about is to use a table per page and push strings for a page to that table as columns and map those tables to the portal. However, this would create 5 additional tables, models and corresponding management of forms. Also, these would be very sparse tables.
Is there a better way for this?
Why have a separate table for each page? It's very likely that terms will be shared across pages and when you add a new page to the app you will then have to add a new table??!!
My preference would just to have one table with the 'term' being the value used in your html and the other being the user preference
table user_strings (
user_id int not null
term varchar not null
val varchar not null
primary key(user_id, term)
foreign key (user_id) references user(id) on delete cascade
index (term)
)
If you are not using composite primary keys, then add a the default id
Assume a table that may look like this:
userId INT (foreign key to a users table)
profileId INT (foreign key to a profiles table)
value INT
Say that in this table preferences for users are saved. The preference should be loaded according to the current user and the profile that the current user has selected. That means that the combination of userId and profileId is unique and can be used as a composite primary key.
But then I want to add the ability to also save a default value that should be used if no value for a specific profileId is save in the database. My first idea would be to set the profileId column to nullable and say that the row that has null as profileId contains the default value. But then I can't use a composite primary key that involves this table, because nullable columns can't be part of a primary key.
So what's the "best" way to work around this? Just drop the primary key completely and go without primary key? Generate an identity column as primary key that I never need? Create a dummy profile to link to in the profile table? Create a separate table for default values (which is the only option that guarantees that no userId has multiple default values??)?
Update: I thought about Dmitry's answer but after all it has the drawback that I can't even create a unique constraint on the two columns userId and profileId (MySQL will allow duplicate values if profileId is null and DB2 will refuse to even create a unique constraint on a nullable column). So with Dmitry's solution I will have to live without this consistency check of the DB. Is that acceptable? Or is that not acceptable (after all consistency checks are a major feature of relational DBs). What is your reasoning?
Create ID autoincrement field for your primary key.
AND
Create unique index for (userId, profileId) pair. If necessary create dummy profile instead of null.
Dmitry's answer is a good one, but since your case involves what is essentially an intersection table, there is another good way to solve this. For your situation I also like the idea of creating a default user profile that you can use in your code to establish default settings. This is good because it keeps your data model clean without introducing extra candidate keys. You would need to be clear in this dummy/default profile that this is what it is. You can give it a clear name like "Default User" and make sure that nobody but the administrator has access to the user credentials.
One other advantage of this solution is that you can sign on as the default user and use your system's GUI to modify the defaults rather than having to fiddle with the data through DB access tools. Depending on the policies in your shop, direct access to the data tables by programmers may be hard or impossible. Using the tested/approved GUIs for modifying defaults removes a lot of red tape and prevents some kinds of accidental damage to the data.
Bottom Line: Primary keys are important. In a transactional system every table should have a at least one unique index one of which should be the primary key. You can always enforce this by adding a surrogate (auto increment) key to every table. Even if you do, you still generally want a natural unique index whenever possible. This is how you will generally find what you're looking for in a table.
Creating a Default User entry in your user table isn't a cheat or a hack, it's using your table structure the way it's meant to be used and it allows you to put a usable unique contraint on the combination of user ID and profile ID, regardless of whether you invent an additional, arbitrary unique constraint with a surrogate key.
This is the normal behaviour of UNIQUE constrain on a NULL column. It allows one row of data with NULL values. However, that is not the behaviour we want for this column. We want the column to accept unique values and also accept multiple NULL values.
This can be achieved using a computed column and adding a contraint to the computed column instead default null value.
Refer below article will help you more in this matter:
UNIQUE Column with multiple NULL values
I always always always use a primary auto_increment key on a table, even if its redundant; it just gives me a fantastically simple way to identify a record I want to access later or refer to elsewhere. I know it doesn't directly answer your question, but it does make the primary key situation simpler.
create table UserProfile ( int UserProfileID auto_increment primary key etc.,
UserID int not null, ProfileID int );
Then create a secondary index UserProfileIDX(UserID, ProfileID) that's unique, but not the primary key.