PostgreSQL 10 Constraint across 2 columns? - php

Is it possible to add a constraint that checks that TWO columns are unique in PostgreSQL 10? I've looked around but I can't find anything related to my specific need.
My issue is that I have to keep track of individual IDs (tag), associated to secondary IDs (hub). I can't set my tag IDs to be unique because they'll appear multiple times associated to hub IDs. I also can't set hub IDs unique because they'll have a bunch of tag IDs associated to them.
Is there a way to say (tag 123 and hub 456) unique, but tag 123 can still be logged if hub is 789, and vice versa?
The root of this is because the ON CONSTRAINT DO UPDATE doesn't seem to work without unique constraints, and I'm now stuck.
References, solutions, other related problems that were solved, any help is appreciated!

You need to define a primary key made of those two columns. This way you can have two or more rows in your table with the same value on the first column of the primary key, but different values on the second column, and vice versa.
Also, if you don't already, you can use pgAdmin, which is a visual tool that helps you create a database and defining tables, columns, data types, constraints, primary keys, etc. with an easy user interface.

indexes can be on multiple columns
CREATE UNIQUE INDEX name ON table (tag,hub);
11.6. Unique Indexes

Related

Should I include auto-incremental id in all related tables?

I have multiple tables in a Laravel app with 1-to-1 relationship such as users , users_settings , user_financial
And some 1-to-many relationships such as users_histories
My questions are:
1. Should I always include incremental id at the first?
for example is the id necessary in the Table #2 below?
Table 1:
id (primary,increments) , name, email, password
Table 2:
id (primary,increments), user_id, something_extra
^ why does every guide include this? // e.g. https://appdividend.com/2017/10/12/laravel-one-to-one-eloquent-relationships/
Can't I just use user_id as primary key and skip the incremental key? because I want to auto insert it on table 2 as soon as data is inserted in table 1.
2. How should I name 1-to-1 and 1-to-many tables in Laravel? `
I searched but didn't find any naming convention for different type of relationships...
Currently I do:
users table with primary key id is the base.
1-to-1: users_settings with foreign key user_id
1-to-many: users_histories foreign_key user_id
many-to-many: users_groups foreign_key user_id
should the first two tables be named settings/setting , histories/history instead? sorry I'm a little confused here.
I actually asked a similar question around 2 days ago. Its up to you but I'd say yes. In my case if I don't auto_increment all my ids in the related tables, data won't be associated with the correct user. However, there is an argument for saying auto_increment columns should not be used in this case, but they are useful for other things. According to some, the relationships might not be as meaningful so it'd be up to you and down to the specifics of you data tables for how meaningful the relationship will be. Regardless, you should research more into the advantages of auto_incrementing all your ids in related tables, as well as possible disadvantages before deciding what you want to do. Either way is fine, but they offer different advantages and disadvantages- which you'll need to compare and what works best for your specific case.
This is a well debated topic about the primary key. IMHO, No, you shouldn't. Every column in database should have a purpose. Following this, for your example, I agree that the auto_increment id is redundant and this is simply because it doesn't have a purpose. The second table is still uniquely describing the user so that the primary key should be the user_id.
Beside the above, there is another principle for me to decide whether I need the auto_increment id: whether I can see a table as an entity. For example, user is clearly an entity, but a relationship is not (in most cases), i.e., composite key can serves the purpose. But when an relationship table is extended to have more attributes and it starts to make sense for it to have an auto_increment id.
I don't have much experience on Laravel, but the naming for a database table should not be dictated by a framework. Comparing history and user_history, what a new DBA or developer expect from the two names without looking its data? user_history describes the table more precisely

Add database "sub-table" per element

I'm not sure if a "sub-table" is the proper term for it, so let me explain a bit better.
I'm setting up a website which contains multiple items, now I've created 2 separate tables in my MySQL database: general and platforms.
My goal now is to split the data of each item into these 2 tables, which works fine so far, but my problem now is the following:
The platforms table has the following structure:
ID
Name
URL
I want to keep track of each item by their ID, so the ID for item #1 should be equal in all tables.
Now, if I have say 3 different platforms for item #1, I'll add every element in the platforms table, but their ID's don't match.
And if I have multiple items, each with multiple platforms it will start to look really messy.
Is it possible to have a table that looks like this?
ID
Name
URL
Hopefully the images clarify it more, basically; I want to have a table that groups together multiple elements.
Is this possible or would I have to do it by assigning a secondary non auto-incrementing ID to each item and manually group the platforms together in PHP?
Looks like you have a one-to-many relationship. Generically, that means
a row in general can be related to zero, one or more rows in platforms.
a row in platforms is related to exactly one row in general.
To implement this design, store the id value from the general table as a foreign key in the platforms table.
id
general_id -- foreign key references id in general table
name
url
Rows in the two tables are related by virtue of a common value.
id general_id name url
--- ---------- --------- --------------------------
77 1 Platform1 http://item1.com/platform1
78 1 Platform2 http://item1.com/platform2
79 1 Platform3 http://item1.com/platform3
To have the database enforce referential integrity, you would need to use a storage engine that supports that (e.g. InnoDB), and you can declare a constraint
ALTER TABLE `platforms` ADD
`general_id` INT NOT NULL COMMENT 'fk ref general.id' AFTER `id`;
(The datatype of the general_id columns must exactly match the datatype of the id column in the general table.)
Before you can enforce the constraint, the values in the new general_id column will have to match a value in the referenced column.
To define the constraint:
ALTER TABLE `platforms`
ADD CONSTRAINT FK_platforms_general
FOREIGN KEY (`general_id`) REFERENCES `general`(`id`)

Do indexes help in queries that don't have the indexed column in where clause?

I want to remove an index in a table whose access in php never uses the indexed column. Index takes up extra space and I am trying to trim it. It's a table of phone numbers. A phone number is linked to a user profile's id. So it has 3 columns. id (index), number and person. I was wondering if removing the index will affect the queries that use number or person in the where clause. My gut feeling is that it shouldn't but I am afraid computer science doesn't work on gut feelings. The data is accessed via joins. For example...
SELECT *
FROM people ... LEFT JOIN
phoneNumbers
ON people.id = phoneNumbers.person
Edit: Apparently no one seems to be able to answer the question in the title.
In the case you show, only the person column would benefit from an index.
Indexes help in basically four cases:
Row restriction, that is finding the rows by value instead of examining every row in the table.
Joining is a subset of row restriction, i.e. each distinct value in the first table looks up matching rows in the second table. Indexing a column that is referenced in the ON clause is done in the same way you would index a column referenced in the WHERE clause.
Sorting, to retrieve rows in index order instead of having to sort the result set as an additional step.
Distinct and Group By, to scan each distinct value in an index.
Covering index, that is when the query needs only the columns that are found in the index.
In the case of InnoDB, every table is treated as an index-organized table based on its primary key, and we should take advantage of this because primary key lookups are very efficient. So if you can redefine a primary key on your phoneNumbers.person column (in part), that would be best.
I think it is a good idea for all tables to have explicit primary keys and an index necessarily comes with these. For instance, it becomes difficult to delete rows in the table, if unwanted duplicates were to appear.
In general, indexes are used for where clauses, on clauses, and order by. If you have an id column, then foreign key references to the table should be using that column, and not the other two columns. The index might also be used for a select count(*) from table query, but I'm not 100% sure if MySQL does this.
If removing an index on a column makes that big a difference, then you should be investigating other ways to make your database more efficient. One method would be using partitioning to store different parts of the database in different files.
If the id column is an auto-incrementing integer, you have already indexed the table in the most efficient way possible. Removing it will make MySQL treat (number, person) as the table's primary key, which will cause less efficient look-ups.
Additionally, any index you create in the future will contain two columns, the first being the indexed field in the desired order, the second being the table's primary key. If you remove the id column and later decide to index the table on person, then your index will be larger than the table itself: each row would be: | person | (number, person) |.
Given that you're querying on this relationship, the person column should be indexed, and leaving the id column in place will ensure that the person index is as small and as quick as possible.
The column "id" seems useless. If I've understood you correctly, I'd
drop the "id" column,
add a primary key constraint on {person, number}, and
a foreign key reference from "person" to people.id.
I'm assuming each person can have more than one phone number.
Creating a primary key constraint has a side-effect that you might not want. It creates an internal index on the key columns.

Active record design pattern[PHP/MySQL]

I refer to the code posted here:
http://codeslayer2010.wordpress.com/2012/04/08/developer-journal-2012-03-30-building-a-php-database-connection-class-from-scratch-singleton-activerecord/
This is all well and good for tables that have a single primary key. But what about tables that have two primary keys such as a composite table? How will the design pattern in the above link account for that? For as you can see, the load function in table.class.php only takes in a single id.
The only thing I can think of for a class representing a composite table is to extend the table class and override the load function, replacing it with one that takes in two ids. But however this seems messy and I was wondering if there was a neater way to accommodate the occurrence of composite tables.
Your thoughts on this matter would be greatly appreciated.
I would recommend to extend the structure of the table to have another column, which is the primary key. And set your current two columns to a combined unique key, so you can still be sure that the data is unique, but call it with one single value as primary key.

In mysql has the same effect in speed terms using a UNIQUE key than a NORMAL key?

For example, I'm doing the next action:
SELECT COUNT(id)
FROM users
WHERE unique_name = 'Wiliam'
// if Wiliam don't exists then...
INSERT INTO users
SET unique_name = 'Wiliam'
The question is, I'm doing the SELECT COUNT(id) check every time I insert a new user, despite of using an unique key or not, so... if "unique_name" has an UNIQUE key it will be better for performance than using a normal key?
What you mean is a UNIQUE CONSTRAINT on the column which will be updated. Reads will be faster, Inserts will be just a bit slower. It will still be faster than your code checking first and then inserting the value though. Just let mysql do its thing and return an error to you if the value is not unique.
You didn't say what this is for, which would help. If its part of an authentication system, then why doesn't your query include the user's password as well? If it's not, a unique indexed column used to store names isn't going to work very well in a real-world system unless you are OK with having just 1 and only Wiliam in your system. (Was that supposed to be William?)
And if that name field is really unique you do not need to use COUNT(ID) in your query. If 'unique_name' is truly unique you either get an id number returned from your query or you get nothing.
You'd want something like this:
SELECT id FROM users WHERE unique_name = 'Wiliam'
No record return, no Wiliam.
An index (unique or non-unique -- I don't know what you're after here) on unique_name will improve the performance.
Your use of 'unique key' isn't very logical so I suspect you are getting confused about the nomenclature of keys, indexes, their relationships, and the purposes for them.
KEYS in a database are used to create and identify relationships between sets of data. This is what makes the 'relational' possible in a relational database.
Keys come in 2 flavors: Primary and foreign.
PRIMARY KEYS identify each row in a table. The value or values that comprise the key must be unique.
Primary keys can be made from a single column or made of several columns (in which case it is called a composite key) that together uniquely identifies the row. Again the important thing here is uniqueness.
I use MySql's auto-increment integer data type for my primary keys.
FOREIGN KEYS identify which rows in a table have a relationship with other rows in other tables. A foreign key of a record in one table is the primary key of the related record in the other table. A foreign key is not unique -- in many-to-many relationships there are by definition multiple records with the same foreign key. They should however be indexed.
INDEXES are used by the database as a sort of short-hand method to quickly look up values, as opposed to scanning the entire table or column for a match. Think of the index in the back of a book. Much easier to find something using a book's index than by flipping through the pages looking for it.
You may also want to index a non-key column for better performance when searching on that column. What column do you use frequently in a WHERE clause? Probably should index it then.
UNIQUE INDEX is an index where all the values in it must be distinct. A column with a unique index will not let you insert a duplicate value, because it would violate the unique constraint. Primary keys are unique indexes. But unique indexes do not have to be primary keys, or even a key.
Hope that helps.
[edited for brevity]
Having a unique constraint is a good thing because it prevents insertion of duplicated entries in case your program is buggy (are you missing a "for update" clause in your select statement?) or in case someone inserts data not using your application.
You should, however, not depend on it in your application for normal operation. Lets assume unique_name is an input field a user can specify. Your application should check whether the name is unique. If it is, insert it. If it was not, tell the user.
It is a bad idea to just try the insert in all cases and see if it was successful: It will create errors in the database server logs that makes it more difficult to find real errors. And it will render your current transaction useless, which may be an issue depending on the situation

Categories