Adding a XOR constraint via Laravel migration - php

I am trying to build a table of this structure using Laravel's migrations feature:
------------------------------------
| Data | Rules | Allow | Restrict |
------------------------------------
| item1 | rule1 | 1,3 | null |
| item2 | rule2 | null | 2,5,6 |
------------------------------------
As in, for each entry either Allow or Restrict must possess a not null value, but not both. I've found this comment which sounds like the condition I need, but I need to express it in a format understandable to Laravel.

I think there are 2 good solutions
Seperate the data in 2 tables
Table1 -> data, rules, constraint (FK)
Table2-> id (PK, referenced by Table1), content (allow/restrict numbers), isAllow(bool)
That way you do the constraint in the database. This is the better solution, because now you don't have null values in your database. Normalisation is a good thing.
Use the event listener to check before insert
https://laravel.com/docs/5.6/eloquent#events
public function creating(Table1 $t1)
{
// check if 1 is null and 1 is not null before creating
}

Related

Database/code design for limiting hierarchical comments at certain level?

I'm making small commenting app written in PHP as backend, React as frontend and PostgreSQL as database. I have table comment which holds all comments and it is self referencing table.
\d+ comment:
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
-----------------+--------------------------+-----------+----------+-------------------------------------+----------+--------------+-------------
id | bigint | | not null | nextval('comment_id_seq'::regclass) | plain | |
website_page_id | bigint | | not null | | plain | |
author_id | bigint | | not null | | plain | |
parent_id | bigint | | | | plain | |
content | text | | | | extended | |
deleted_date | timestamp with time zone | | | | plain | |
updated_date | timestamp with time zone | | not null | | plain | |
created_date | timestamp with time zone | | not null | | plain |
On the client side I make request to get all comments, backend makes recusrive query to database to grab all comments and return them in appropriate format, then I render it.
Here is JSON of parent comment:
{
id: 1
author_id: 1
content: "Some content"
created_date: "2019-05-29 06:11:43+00"
depth: 0
parent_id: null
replies: [...]
updated_date: "2019-05-29 06:11:43+00"
website_page_id: null
}
So each comment as depth parameter, which I use to define identation (I don't nest comments recursively like comment -> replies -> comment -> replies, it is only comment and all its replies. I do extra processing on backend to make this form, PostgreSQL returns just data as it is with depth definition.
I have a form for creating new comments and replies to existing comments. So far replies can nest as far as it can go (not sure about database limitations).
Here are my concerns:
I don't want to nest forever as it kills performance (I assume). Does it really? Also, it is resonable to limit it up to n level by default so it does not go off the screen on the client side.
Not sure where and how to make limitation. Whether it should be on the database level, backend or client side?
I had only one idea how to solve it, but so far it does not seem to be elegant solution. Here it is:
Ignore that it nests on the database level and just limit identation on client side, so if I defined 5 level as maximum, then anything above that would have 5 level identation. It works, but it does not help the database performace.
I am pretty sure there are other possible ways to do this, help would be appreciated!
Recursive queries (when they take advantage of index) are really fast. It will probably take more time to nest the results in Javascript. The nesting limitation is more for the UI and not very difficult to fetch:
with recursive
comment_node (comment_id, parent_id, level) as (
select comment_id, comment_parent_id, 1::int4 as level
from comment
where website_page_id = $*
union all
select c.comment_id, c.comment_parent_id, parent.level + 1 as level
from comment as c
inner join comment_node as parent
on parent.comment_id = c.parent_id
and parent.level < 5
)
select c.comment_id, cn.level, c.comment_parent_id, c.content, a.name, ...
from comment as c
join comment_node as cn
using (comment_id)
join author as a
using (author_id)
Limiting the insertion of comments with a nesting level of 5 or more is probably not a meaningful database constraint as it does not break the data consistency.

finding next ID in a CHAR field without AUTO_INCREMENT

I got a Table which stores objects. An Object can be anything from a chair to a employee. An Object got an ObjectID, which is a 10 characters code-39 barcode label on the Object.
Many Objects already have a Label, thus an ObjectID assinged to them. Some have Prefixes, e.g. "9000000345" might be a Desk or "0000000895" might be a folder with invoices.
When People start a new Folder for example, they take pre-printed Barcode Labels
and put them on it. The pre-printed Barcode Labels are generated by a Printer which just increases a number by 1 and zerofills it to 10 Digits and then prints it as code-39.
All Most of the objects are stored in Excel Sheets. They now should be migrated into a MySQL Database.
Now, the System should also be able to create objects on its own. Objects created by the System have a leading "1" e.g. "1000000426".
The Problem: How do I get the next ObjectID for Auto generated Objects?
I cant really use AUTO_INCREMENT because there are also non-auto-generated rows in the table.
Another Thing to say is that the 'ObjectID' field has to be CHAR(10) because for special occasions there were alphanumeric prefixes used like "T1" -> "T100003158"
My Table when using AUTO_INCREMENT:
| ID | Created | ObjectID | Parent | Title | Changed | Note |
|----|-------------|--------------|--------|-------------|-------------|------|
| 1 | <timestamp> | "1000000001" | NULL | "Shelf 203" | <timestamp> | NULL |
| 2 | <timestamp> | "9000000458" | NULL | "Lamp" | <timestamp> | NULL |
| 3 | <timestamp> | "1000000003" | NULL | "Shelf 204" | <timestamp> | NULL |
The ObjectID of the last Object in the table should be "1000000002" not "1000000003"
I hope I could explain the Problem well enough.
Naive solution can be:
SELECT CAST(ObjectID AS UNSIGNED) + 1 FROM yourTable WHERE ObjectId LIKE "1%" ORDER BY ObjectID DESC LIMIT 1
Basically search for all Object ID starting with 1xxxx then sort them (because its zero padded we can still sort) and then cast result to int and increment it.
Might be faster to cast to int first and then do between. Rest would be the same

PHP/MYSQL: Storing a list or massive table

I am still new to PHP and I was wondering which alternative would be better or maybe someone could suggest a better way.
I have a set of users and I have to track all of their interactions with posts. If a users taps on a button, it will add the post to a list and if they tap it again, it will remove the post, so would it be better to:
Have a column of a JSON array of postIDs stored in the table for each user (probably thousands).
-or-
Have a separate table with every save (combination of postID and userID) (probably millions) and return all results where the userID's match?
For the purposes of this question, there are two tables: Table A is users and Table B is posts. How should I store all of the user's saved posts?
EDIT: Sorry, but I didn't mention that posts will have multiple user interactions and users will have multiple post interactions (Many to Many relationship). I think that would affect Bob's answer.
This is an interesting question!
The solution really depends on your expected use case. If each user has a list of posts they've tagged, and that is all the information you need, it will be expedient to list these as a field in the user's table (or in their blob if you're using a nosql backend - a viable option if this is your use case!). There will be no impact on transmission time since the list will be the same size either way, but in this solution you will probably save on lookup time, since you're only using one table and dbs will optimize to keep this information close together.
On the other hand, if you have to be able to query a given post for all the users that have tagged it, then option two will be much better. In the former method, you'd have to query all users and see if each one had the post. In this option, you simply have to find all the relations and work from there. Presumably you'd have a user table, a post table and a user_post table with foreign keys to the first two tables. There are other ways to do this, but it necessitates maintaining multiple lists and cross checking each time, which is an expensive set of operations and error-prone.
Note that the latter option shouldn't choke on 'millions' of connections, since the db should be optimized for this sort of quick read. (pro tip: index the proper columns!) Do be careful about any data massage, though. One unnecessary for-loop will kill your performance.
For the purposes of this question, there are two tables: Table A is users and Table B is posts. How should I store all of the user's saved posts?
If each user has a unique ID of some sort (primary key), then ad a field to each post that refers to the unique ID of the user.
mysql> describe users;
+----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| email | varchar(200) | YES | | NULL | |
| username | varchar(20) | YES | | NULL | |
+----------+------------------+------+-----+---------+----------------+
mysql> describe posts;
+---------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------+------+-----+---------+----------------+
| id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| user | int(11) unsigned | NO | | NULL | |
| text | text | YES | | NULL | |
+---------+------------------+------+-----+---------+----------------+
Then to get posts for a user, for example:
SELECT text
FROM posts
WHERE user=5;
Or to get all the posts from a particular organization:
SELECT posts.text,users.username
FROM posts,users
WHERE post.user=users.id
AND users.email LIKE '%#example.com';
I think it would make sense to keep a third table that would be all the post status data.
If your user interface shows, say, 50 posts per page, then the UI only needs to keep track of 50 posts at a time. They'll all have unique IDs in your database, so that shouldn't be a problem.

Speed Up MySQL (MyISAM) COUNTs with WHERE Clauses

We are implementing a system that analyses books. The system is written in PHP, and for each book loops through the words and analyses each of them, setting certain flags (that translate to database fields) from various regular expressions and other tests.
This results in a matches table, similar to the example below:
+------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| regex | varchar(250) | YES | | NULL | |
| description | varchar(250) | NO | | NULL | |
| phonic_description | varchar(255) | NO | | NULL | |
| is_high_frequency | tinyint(1) | NO | | NULL | |
| is_readable | tinyint(1) | NO | | NULL | |
| book_id | bigint(20) | YES | | NULL | |
| matched_regex | varchar(255) | YES | | NULL | |
| [...] | | | | | |
+------------------------+--------------+------+-----+---------+----------------+
Most of the omitted fields are tinyint, either 0 or 1. There are currently 25 fields in the matches table.
There are ~2,000,000 rows in the matches table, the output of analyzing ~500 books.
Currently, there is a "reports" area of the site which queries the matches table like this:
SELECT COUNT(*)
FROM matches
WHERE is_readable = 1
AND other_flag = 0
AND another_flag = 1
However, at present it takes over a minute to fetch the main index report as each query takes about 0.7 seconds. I am caching this at a query level, but it still takes too long for the initial page load.
As I am not very experienced in how to manage datasets such as this, can anyone advise me of a better way to store or query this data? Are there any optimisations I can use with MySQL to improve the performance of these COUNTs, or am I better off using another database or data structure?
We are currently using MySQL with MyISAM tables and a VPS for this, so switching to a new database system altogether isn't out of the question.
You need to use indexes, create them on the columns you do a WHERE on most frequently.
ALTER TABLE `matches` ADD INDEX ( `is_readable` )
etc..
You can also create indexes based on multiple columns, if your doing the same type of query over and over its useful. phpMyAdmin has the index option on the structure page of the table at the bottom.
Add multi index to this table as you are selecting by more than one field. Below index should help a lot. Those type of indexes are very good for boolean / int columns. For indexes with varchar values read more here: http://dev.mysql.com/doc/refman/5.0/en/create-index.html
ALTER TABLE `matches` ADD INDEX ( `is_readable`, `other_flag`, `another_flag` )
One more thing is to check your queries by using EXPLAIN {YOUR WHOLE SQL STATEMENT} to check which index is used by DB. So in this example you should run query:
EXPLAIN ALTER TABLE `matches` ADD INDEX ( `is_readable`, `other_flag`, `another_flag` )
More info on EXPLAIN: http://dev.mysql.com/doc/refman/5.0/en/explain.html

What is a Parent table and a Child table in Database?

I just want to know what is a parent table and what is a child table in databases. Can you please show me an example so I understand how it works please.
Thank You
Child tables and parent tables are just normal database tables, but they’re linked in a way that's described by a parent–child relationship.
It’s usually used to specify where one table’s value refers to the value in another table (usually a primary key of another table).
For example, imagine a news article. This could be represented by a table called articles and has fields for id, headline, body, published_date and author. But instead of placing a name in the author field, you could instead put the ID value of a user in a separate table—maybe called authors—that has information on authors such as id, name, and email.
Therefore, if you need to update an author’s name, you only need to do so in the authors (parent) table; because the articles (child) table only contains the ID of the corresponding author record.
Hope this helps you understand better.
Be aware you can have relationships that appear to be parent-child but are not, for instance when lookup tables are being used. The distinction is that in a true parent-child relationship, records typically don't stand are their own very well - they are detail records for the parent and are not useful without the parent table info. A person can own multiple cars in the DMV database, but you wouldn't want records in the CARS table without a parent record in the OWNERS table - it would be nearly useless data.
On the other hand, if I am using a lookup table to expand a code to something more meaningful, or to constrain data entry to acceptable values, then the "child" record can still useful (can stand alone) if the lookup table is deleted. I could still have the sex information as "M" or "F" even if I no longer have the lookup table to expand that to "Male" or "Female".
Parent - The entity on the "one" (/1) side of a relation with another table
Child - The entity on the "many" (/N/*) side of a relation with another table
A child table tends to be one where it has one or more foreign keys pointing at some other table(s). Note that a child table can itself be a parent to some OTHER table as well.
Those terms are used in database relationships.
for example u have two table,
1.Manifast
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| manifast_id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| description | text | NO | | NULL | |
| title | text | NO | | NULL | |
+-------------+------------------+------+-----+---------+----------------+
day_sequence
+-----------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------------+------+-----+---------+----------------+
| day_sequence_id | int(11) unsigned | NO | PRI | NULL | auto_increment |
| day_number | int(11) | NO | | NULL | |
| day_start | int(11) | NO | | NULL | |
| manifast_id | int(11) | NO | | NULL | |
+-----------------+------------------+------+-----+---------+----------------+
if u want to connect those two tables,u need to use the command with following format.
> ALTER TABLE child_table_name ADD FOREIGN KEY (P_ID) REFERENCES
> parent_table_name (P_ID)
and so it become.
> ALTER TABLE day_sequence ADD CONSTRAINT fk_manifast FOREIGN KEY
> (manifast_Id) REFERENCES manifast(manifast_Id);
In summary,
Child table is a table which has foreign key,and is connected from others table.
Parent table has no foreign key and connect to other.
[ Note : This ans is just for connecting two tables ]

Categories