How to synchronize pivot table in plain PHP - php

Assume we have these tables
Users table
-----------
id name
-----------
1 xxx
2 yyy
3 ccc
4 bbb
5 aaa
Location table
-------------
id name
-------------
6 Spain
7 Russia
8 Germany
9 USA
Pivot table
------------------------
id user_id location_id
------------------------
1 1 6
2 2 8
3 1 8
4 1 9
5 3 8
What I want to achieve is to sync the data in the pivot table.
So exactly I have post request with array of user ids = [1,5,4] and location_id = 8. So I would get the following result
[updated] Pivot table
-------------------------
id user_id location_id
-------------------------
1 1 6 <-- This one stays
3 1 8
4 1 9
6 5 8 <-- Added
7 4 8 <-- Added
...we deleted the row with location_id=8 and user_id=2 and user=3 because those are not in the users array
How can I do add the new ones, delete the ones that are not in the request, and leave the one that already exists with some functionality, just like Laravel has done it in sync function.
I know that the easyest way is to get all the users that has that specific location_id, delete all, and than insert once again. Is there some workaround or should I do the newbie way :D
Thank you

Not sure if this is the best way to go (actually deleting everything and then inserting the values seems easier and cleaner), but here's one way to do it, if for some reason you want to keep the pivot table ID:
Define a unique index for the table, considering both pivot fields (user_id,location_id).
Insert all data using ON DUPLICATE KEY UPDATE user_id = user_id and location_id = location_id, so that if there's duplicate values, mysql will keep them without modifications and will not throw error.
Implode the Array IDs from the Request and execute a delete from the table, where data is not in the specified values.

Related

Re assign foreign ID without conflict

I have a main table with a primary key (ÍD) and a linked one.
Existing Data
Main
ID | name
=========
1 | foo
2 | bar
3 | loo
4 | zoo
Linked
main_id
=======
1
1
2
2
There are connections to the first entries of main (1,2) in the linked table.
Now new data gets imported from the same structure:
Import Data
ID | name
=========
1 | new_foo
2 | new_bar
3 | new_loo
4 | new_zoo
and
main_id
=======
3
4
3
1
During the import process the IDs of the main table will get new ids (done by a script)
ID | name
=========
1 | foo
2 | bar
3 | loo
4 | zoo
5 | new_foo
6 | new_bar
7 | new_loo
8 | new_zoo
but the main_id will still have the ids from the imported data:
main_id
=======
1
1
2
2
3 => should be 7
4 => should be 8
3 => should be 7
1 => should be 5 => * comment below
*I cannot simple update linked like:
UPDATE linked SET main_id = 5 WHERE main_id = 1
as it would update the first two rows as well.
So how can I map these field with the new primary ID of main?
I could add a high number to the main_id before import like
UPDATE linked SET main_id = main_id + 10000000
do the import
apply the real ID
UPDATE linked SET main_id = %realID WHERE main_id = %importedID
return my temporary ID back to the original.
UPDATE linked SET main_id = main_id - 10000000 WHERE main_id > 10000000
The problem is pretty obvious: This doesn't work well (or at all) if the IDs are higher than 10.000.000 or the temporary id is higher than BIGINT (9223372036854775807).
It could work with a clone of the table but this may cause a problem in memory consumption as the linked table can get pretty big.
I'm sure there's a "best practice" way of doing this.
The steps are:
Pick last ID in Main table: select #lastId := max(ID) from Main;
Just shift all IDs, that are to be written in Linked table, by #lastId.
Something like this: insert into Linked select ID + #lastId from Imported_linked_data
No extra steps needed.
You can add the new entries in the Main table and let the AUTO_INCREMENT column generate the Id of the new row (as it should). For each row you cache the generated Id in an associative array, which maps the old Id to the new generated Id.
After that you insert the entries for the linked table. Before adding the new row you replace the old foreign key Id to the new generated id based on your created associative array.

Updating Database From Static File JSON Feed

I have a PHP script pulling a JSON file that is static and updates every 10 seconds. It has details about some events that happen and it just adds to the top of the JSON file. I then insert them into a MySQL database.
Because I have to pull every event every time I pull the file, I will only be inserting new events. The easy way would be to search for the event in the database (primary keys are not the same), but I am talking about ~4000 events every day, and I do not want that many queries just to see if it exists.
I am aware of INSERT IGNORE, but it looks like it only uses PRIMARY_KEY to do this.
What can I do (preferably easily) to prevent duplicates on two keys?
Example:
I have a table events with the following columns:
ID (irrelevant, really)
event_id (that I need to store from the source)
action_id (many action_ids belong to one event_id)
timestamp
whatever...
And my data is my JSON comes out on the first pull like this:
event_id|action_id|...
1 | 1
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
Then the next pull is this:
event_id|action_id|...
1 | 1
1 | 2
1 | 3
1** | 4**
1** | 5**
2 | 1
2 | 2
2 | 3
2** | 4**
I only want the rows marked with asterisks to be inserted, and the others to be ignored. Remember, primary_key column id is completely in this table, and I just use it for ubiquity.
What command can I use to "INSERT" every event I pull, but ONLY adding those that aren't duplicated by way of the two columns event_id and action_id.
Thanks.
Create a unique index of both columns.
CREATE
UNIQUE INDEX event_action
ON tablename (event_id, action_id)

Mysql find a table in from another table record and find a specific record in this table

I'm a new in Mysql and I have a complicated problem:
I have a table with "Shops" name in this table there is a ShopID column. The records look like this:
Shop_001
Shop_002...
Every "shopID" refer to a new table with this name, for example there is a table with Shop_0001 name. In this table there is "partnumber" column which mean the parts which are available in this shop.
I send a specific part number to sql server and I want to check all shops in the "Shops" table and return a rows in the "Shop_xxxx" tables which has this specific partnumber.
Unfortunately I have no idea how do I get start on this. Can anybody help me give some instruction or anything on this?
you're looking for a many to many relationship. so you just need 3 tables
1 table is the list of shops
1 table is the list of products
and 1 table is the list of which shops have which products. like this
table1
id|shops
------
1 shop1
2 shop2
3 shop3
table2
id|products
------
1 prod1
2 prod2
3 prod3
4 prod4
5 prod5
table3
id|shop_id|prod_id
-------------------
1 2 3
2 2 1
3 2 2
4 1 3
5 1 4
6 1 5
7 3 2
So for every time a product is added to a shop, an entry is added in table3. This will allow you to query by shops or by products, and you will only ever need 3 tables.
google querying many to many relationships for how to get the list of products for shop1 or the list of shops that have product4 etc.

MySQL Query problem (duplicate results)

Im having a problem finding duplicate results in a mysql database (a cocktail recipe website). Here the setup:
Table 1: 'cocktail'
[cid,c_name] (cid = unique cocktail id, c_name = cocktail name)
Table 2: 'ingredients':
[iid,i_name] (iid = unique ingredient id, i_name = ingredient name)
Table 3: 'cocktail_ingredients' (the linking table)
[ciid,cid,iid] (ciid = unique row identifier, cid = cocktail cid, iid = ingredient iid)
So one cocktail can have multiple rows in the 'cocktail_ingredients' table (1 to many).
Setup is fine. The problem Im having now is finding if there are duplicate cocktails in my database.
For instance if the cocktail_ingredients table had these entries:
cid | iid
1 | 56
1 | 78
1 | 101
.
.
.
9 | 56
9 | 78
9 | 101
The cocktail is the same (for theoretical purposes here anyway).
If the 'cocktail_ingredients' table had one more row ...
9 | 103
Then it wouldn't be the same, as cocktail number 9 includes an extra ingredient.
So the mysql has to do 2 checks, firstly that the ingredient count is the same, and secondly that every ingredient id (iid) is the same for corresponding cocktails (cid).
Im stumped on this one, any help much appreciated. I'm thinking I might have to head down the PHP route as well to code in something more complex, but I'm struggling there as well so thought this would be a good place to stop and ask.
Thanks a ton
Nick
You may recall from a distant math class that the definition of set equality is that both A abd B are subsets of one another (non-strict) so just create a view or procedure that checks if every thin that is in A is also in B, then check the two cocktails are both subsets of one another. This is far from a complete answer, but it may be enough to get you going ;)
it will probably be easier to do the negation - find an ingredient in A that is not in B. none exist, then A must be a strict subset of B (assuming A and B can't both be empty)
Alternatively do a count of each ingredient in A, each ingredient ion B and each ingredient in A and B then if they are equal they are equivalent cocktails
CREATE VIEW ingredient_count AS
SELECT cid, count(*) as ingredients
FROM cocktail_ingredients
GROUP BY cid
CREATE VIEW shared_ingredients AS
SELECT c1.cid cid1, c2.cid cid2, count(*) as ingredients
FROM cocktail_ingredients as c1 INNER JOIN cocktail_ingredients as c2
ON (c1.cid != c2.cid AND c1.iid = c2.iid)
GROUP BY c1.cid,c2.cid
CREATE VIEW duplicates AS
SELECT cid1,cid2
FROM (ingredient_count AS ic1 INNER JOIN shared_ingredients
ON ic1.cid=cid1) INNER JOIN ingredient_count as ic2
ON ic2.cid=cid2
WHERE ic1.ingredients=ic2.ingredients
AND shared_ingredients=ic1.ingredients
Note this may be much faster in mysql with subselects with sensible where clauses rather than views, but this is easier to understand
You can impose such checking using TRIGGER.
But, yet there is a conceptual problem.
Say, you have two cocktails {1 | 56, 78, 101} and {9 | 56, 78, 101, 103} and also assume that you have implemented the check.
Now, you are inserting data for 1:
cid | iid
----------
1 | 56
Then, add rest two ingredients...
cid | iid
----------
1 | 56
1 | 78
1 | 101
Fine, now you start adding 9:
cid | iid
----------
1 | 56
1 | 78
1 | 101
9 | 56
You have three more ingredients, so continue adding them:
cid | iid
----------
1 | 56
1 | 78
1 | 101
9 | 56
9 | 78
Two more remaining (101,103)
But alas! You cannot add 101! If you try to add 101, then 9 would become identical to 1, which your trigger will prevent you from adding.
When a cocktail is subset of another, you have to add the subset later. I hope I could make you understand this.
You should not put any restriction in database. What I would do in my web application is:
In the cocktail entry/update interface, I would take user input (and not yet insert/update in DB)
When user clicks the save button (I would add a save button), check if the new/updated cocktail becomes copy of another (May be I would write a stored procedure, but it can be found using a select query only)
If the new/updated cocktail is not duplicate of another, insert/update database. If

How to deal with a large mySQL tree

I am working on a project which user registration multiples like this.
1st Month -> 1 user
2nd Month -> 4 users comes under the above user
3rd Month -> 16 users (i.e, 4 users comes under each 4 users above )
4th Month -> 64 users (i.e, 4 users comes under each 16 users above )
eg:
1
2 | 2
8 | 8
32| 32
and continues...
Please give me an advice, how to store this in database.
Thanks in advance.
Assuming user_id is your primary key, create a parent column that contains the user_id of the parent user. For example:
user_id parent
1 NULL
2 1
3 1
4 1
5 1
6 2
...
You will also want to create an index on the parent column so that you can quickly do a reverse lookup (i.e. find all children of a given user).

Categories