Graphcool: how to create multifield unique constraints? - unique

I'd like to create multi field constraint. In the same way in which is possible in SQL for instance by specifying:
CREATE TABLE …. UNIQUE(field1, field2);
So that what is unique is the combination of fields.
Is it possible to enforce this constraint in Graphcool?

You can use permission queries to achieve custom checks.
For example you dont want that user be able to add multiple comments on Post, so you want that Comment.post_id and Comment.user_id be unique. Use this permission query for this
SomeProposalExists(filter: {
post: {
id_not: $input_postId
}
user: {
id_not: $input_userId
}
})

Related

Mysql: possible to add constraint that prevents a one to many relation from having less than certain number of relations?

I have a user table that has many, say user_property table, where the foreign user_id is stored in the user_property table.
Now is it possible to add constraint so that a user should have at least one user property? So when a user have five properties, he can delete it one by one, but when there is only one property left, he can not delete it? I tried Googling but I am not even sure what is the search keyword for this.
The reason is, I want to avoid checking if a user have one property remaining only from the application layer, because it reads from replica, the read and write might not be synchronized, and on certain condition the user might accidentally delete all properties.
Any suggestion or different approaches is appreciated.
I don't think you can do this with a constraint. The problem is handling new users. You cannot insert a new user, because it has no properties. You cannot insert a new property, because the user reference is not valid. Ouch!
One solution involves triggers. The idea is the following:
Add to the the users table a column for the number of current properties.
Add to the users table a column for the maximum number of properties ever.
Default the two values to 0 for new users.
Add a check constraint (or trigger) that when the maximum is > 0 then the current number has to be > 0.
In any database, you need to implement the first two counts using triggers (on user_property). MySQL does not support check constraints, so the last condition also requires a trigger.
There is no constraint in SQL that does what you describe.
A foreign key constraint would ensure that every row in user_property must reference a row that exists in the user table.
But there is no constraint in SQL that does the reverse: ensure every user is referenced by at least one row in user_property.
A CHECK constraint has been mentioned by some other comments and answers. But a CHECK constraint can reference only columns of the same row. It can't reference other rows of the same table or different tables.
The most straightforward solution is to handle this in application code. That is:
Implement a function that INSERTs to user, while making sure there's also an INSERT of the first row to user_property.
Implement a function that DELETEs from user_property, but first check if it's would leave zero properties for the given user_id. If so, return an error instead of deleting the user property.
Implementing such data integrity rules in application code comes with a risk, of course. What if you have multiple apps that access the same database? You need to implement the same rules in different apps. Perhaps even in different programming languages. Sounds like a PITA.
Nevertheless, not all business rules can be implemented with simple SQL declarative constraints.

Foreign key to reference all records or no records in junction table

This seems like a desirable feature but I can't seem to figure out how I would do it while the foreign key is a part of the primary key (composite key).
The table is a simple junction table for a many to many relationship referencing User.id and Access.id referencing functions a user has access to:
Column | References
user user.id
access access.id
Therefore there can be many entries for each user and each access level. To simplify things for "superusers" I wanted to have a NULL value for access which would mean they have access to every page (this is the only way I could figure how to enter a value that didn't reference a row in the access table). The problem is MySQL won't allow a NULL value as a part of the primary key.
Is there a way around the NULL issue with primary keys or is there another way to reference every row (or no rows) in a foreign key? The only other way around this I can think of would be to disable the foreign key or have to add a row for every access.id in the table. Either of which would be undesirable.
Presumably you have a superuser flag on your user table. You could UNION a Cartesian join of each superuser and the set of available access IDs into whatever query you need this for.
Depending on what you're doing, you could also just not store the access for a superuser in the database and treat them differently in code - i.e. ignore the access check once you've established them as SU. Depends on your application though.
I think NULL is allowed and you can use it as a unique combination along with user.id. But I am not sure if this is a good way to do this. I mean you can store the super user setting in a column and use it in the code than here.

Sql simple beginner operation

I have a table named USERS with user_id as primary key and user_name.
I have another table USERS_ACT with user_act_id primary key, user_act_user_id and another 2 columns.
I need user_act_user_id to be foreign key in USERS? How can I achieve this?
This is my first day in SQL so please be kind to explain if what I ask is wrong.
let's assume you are not the DB admin and you just want to get all the active users' names ;))
select users.user_name
from users
join users_act on users.user_id = users_act.user_act_user_id
Without referencial integrity it's up to you to make it work, there's no "magic" around it.
Populate your user_act_user_id with a pk-value from USERS and there you have it.
You may want to add constraints, but that may not be what you're asking for,
http://msdn.microsoft.com/en-us/library/ms175464.aspx
In short, they keep the keys between tables in good shape.
Assuming you are using InnoDB (which is the only engine that supports foreign keys):
ALTER TABLE users_act
ADD CONSTRAINT fk_users_act_users
FOREIGN KEY (user_act_user_id)
REFERENCES users (user_id);
It depends on your DB Type if MySql even supports foreign keys. For example you can use foreign keys with InnoDB format but not with MyIsam format.
When working with MySql i personally prefer working with MyIsam and do most of the checking about integrity while programming.
In general you can just add user_act_user_id in your table USERS but not mark it as any key. After that you can simple use a JOIN, but ofc the referencial integrity is not given so have to write your own "trigger" on programming site if you want f.e. to automaticly delete data belonging to a user in the other table. Otherwise you have to use constraints or triggers, but this might be not that easy when just started with SQL.

How to restrict a column value in SQLite / MySQL

I would like to restrict a column value in a SQL table. For example, the column values can only be "car" or "bike" or "van". My question is how do you achieve this in SQL, and is it a good idea to do this on the DB side or should I let the application restrict the input.
I also have the intention to add or remove more values in the future, for example, "truck".
The type of Databases I am using are SQLite and MySQL.
Add a new table containing these means of transport, and make your column a foreign key to that table. New means of transport can be added to the table in future, and your column definition remains the same.
With this construction, I would definitively choose to regulate this at the DB level, rather than that of the application.
For MySQL, you can use the ENUM data type.
column_name ENUM('small', 'medium', 'large')
See MySQL Reference: The ENUM Type
To add to this, I find it's always better to restrict on the DB side AND on the app side. An Enum plus a Select box and you're covered.
Yes, it is recommended to add check constraints. Check constraints are used to ensure the validity of data in a database and to provide data integrity. If they are used at the database level, applications that use the database will not be able to add invalid data or modify valid data so the data becomes invalid, even if the application itself accepts invalid data.
In SQLite:
create table MyTable
(
name string check(name = "car" or name = "bike" or name = "van")
);
In MySQL:
create table MyTable
(
name ENUM('car', 'bike', 'van')
);
You would use a check constraint. In SQL Server it works like this
ALTER TABLE Vehicles
ADD CONSTRAINT chkVehicleType CHECK (VehicleType in ('car','bike','van'));
I'm not sure if this is ANSI standard but I'm certain that MySQL has a similar construct.
If you want to go with DB-side validation, you can use triggers. See this for SQLite, and this detailed how-to for MySQL.
So the question is really whether you should use Database validation or not. If you have multiple clients -- whether they are different programs, or multiple users (with possibly different versions of the program) -- then going the database route is definitely best. The database is (hopefully) centralized, so you can decouple some of the details of validation. In your particular case, you can verify that the value being inserted into the column is contained in a separate table that simply lists valid values.
On the other hand, if you have little experience with databases, plan to target several different databases, and don't have the time to develop expertise, perhaps simple application level validation is the most expedient choice.
To add some beginner level context to the excellent answer of #NGLN above.
First, one needs to check the foreign key constraint is active, otherwise sqlite won't limit to the input to the column to the reference table:
PRAGMA foreign_key;
...which gives a response of 0 or 1, indicating on or off.
To set the foreign key constraint:
PRAGMA foreign_keys = ON;
This needs to be set to ensure that sqlite3 enforces the constraint.
I found it simplest to just set the primary key of the reference table to be the type. In the OP's example:
CREATE TABLE IF NOT EXISTS vehicle_types(
vehicle_type text PRIMARY KEY);
Then, one can insert 'car', 'bike' etc into the vehicle_types table (and more in the future) and reference that table in the foreign key constraint in the child table (the table in which the OP wished to reference the type of vehicle):
CREATE TABLE IF NOT EXISTS ops_original_table(
col_id integer PRIMARY KEY,
...many other columns...
vehicle_type text NOT NULL,
FOREIGN KEY (vehicle_type) REFERENCES vehicle_types(vehicle_type);
Outwith the scope of the OP's question but also take note that when setting up a foreign key constraint thought should be given to what happens to the column in child table (ops_original_table) if a parent table value (vehicle_types) is deleted or updated. See this page for info

Alter MediaWiki's user table

How can I change the default MySQL table "user" , that's used to store all members?
is it more of a database question? in mysql you could issue command, for example:
ALTER TABLE user ADD COLUMN user_birthday DATE;
check the actual name of user table. MW uses table prefix option which may not be empty.
The actual statement will depend on what column you want to add of course.
However, for the better portability of your wiki (e.g. easier upgrade) you might want to create a new table instead for the user profile that would have user_id as FOREIGN_KEY
Also there is a field user_options which stores name=value pairs of extra data. - you can make use of that if you don't care about searching your DB against the new "field".
Do not.
If you need to alter the user table (and related) server-side, use the appropriate maintenance script: createAndPromote.php is among the most used, there are others for specific functions.
You didn't specify your use case, but chances are that you're going to break MediaWiki in unexpected ways if you try to manually alter the table.
Here is some information about the "user" table.