Need optimized sql advice - mysql

So i have this database full of tables like suppliers, clients, stores, store_users, services(of each store), repairs, etc etc (a database of a IT brand that repairs computers at each store).
In the "repairs" table i have fields regarding the client, hardware, breakdown, condition and state of the repair("started", "waiting for client answer", "delivered" and others).
Everytime there are changes in the repair, for example: user1 received the repair order and inserted the repair into the system. user2 tested harddisk, RAM, etc for problems, found major disk problem so it needs to be replaced.
This information will change many fields in the "repair" but i need something like another table "interventions" to know what changed and who did what in each time there was an edit to the repair.
The only idea i had was to make a new table "interventions" with all the fields from the "repairs" table and every time someone edited the repair it would copy it to the intervention with a id_repair and a id_user linked.
PS: Users are the store's employees

It sounds like your "repair" table needs to be renamed to "interventions" and what you currently see as the "repair" table changed to a view which shows the most recent intervention.

Related

organizing multi-tenant db/MySQL [SaaS]

Good example will be shopify. Where you have N number of users (in this case each user assume site). And each user will have it's own records in DB. But db schema will be the same (same tables for each user, products, customers, orders etc.).
So question is what will be the best way to organize this kind of solution?
Store everything in one DB but in a different tables, or run separate DB for each user (but then will be question with maintaining, scalability and automatization)
possible solution:
We can use one DB with common tables like products, customers, orders etc. And we will have table users where we store records about each site.
In tables products, customers we will group all records by user_id.
This is one of possible solutions. But if we will have 1000 users (sites), each will have ~2k products, and ~100k customers, we can end up with tables which has millions of records, so questions will be:
how it will perform compare to each user (site) would have it's own DB?
how reliable this approach? bigger data, harder maintain, backup/restore
safety, if something wrong with one source thousands will be affected
Any links etc. will be much appreciated, thanks!
Create a mysql user for each tenant
Add a tenant_id column to each table
Add a view for each table that filters based on tenant_id = mysql_user
Use a trigger to automatically populate the tenant_id column on INSERT
Restrict the tenant mysql users to only access the views, not the raw tables
I wrote up a blog post on how I was able to convert a large single-tenant mysql application to a multi-tenant application in a weekend using this technique.
https://opensource.io/it/mysql-multi-tenant/
I recommend reviewing databases by well-supported open source solutions. With this in mind, here's a pretty simple schema I found real quick that'd explain a good working solution for this with scale-ability in mind.
http://www.zentut.com/sql-tutorial/sql-sample-database/
I have this file Generate_multiTanentMysql.php i do all steps with PHP script
https://github.com/ziedtuihri/SaaS_Application
Solution Design Pattern :
Creating a database user for each tenant
Renaming every table to a different and unique name (e.g. using a prefix ‘someprefix_’)
Adding a text column called ‘id_tenant’ to every table to store the name of the tenant the row belongs to
Creating a trigger for each table to automatically store the current database username to the id_tenant column before inserting a new row
Creating a view for each table with the original table name with all the columns except id_tenant. The view will only return rows where (id_tenant = current_database_username)
Only grant permission to the views (not tables) to each tenant’s database user Then, the only part of the application that needs to change is the database connection logic. When someone connects to the SaaS, the application would need to:
Connect to the database as that tenant-specific username

Export (backup) and Truncate (delete) table at the same time (i.e. atomic)?

I am hosting a forum with "forum gold".
People trade this a lot, gift it, award people with it to "thank" or "like" posts, or to increase reputation.
However, I am concerned that there might be some exploit that allows people to hack gold into their forum account, so I added logging on EVERY forum gold transaction.
It works well. I can perform sum queries to assure that no unknown sources are introducing forum gold into the system, and to ensure that all forum gold awarded to users are accounted for.
However, it totally blew up. Within just a couple of days, I have more than 100,000 entries in the table. I also got mail from my webhost about a slow mySQL query warning, which is just a simple SELECT from that table of a single record, no joins, ordering, functions like date_add() or anything at all even.
So I want to completely export AND empty the table with the logs. Now, I normally back up the rest of my database via the "export" feature in phpmyadmin. However, this table is highly active, anywhere from 10 up to 50 new rows are added every second, but I want to keep the integrity and accuracy of my computations by not losing any records.
Is there an "atomic" way I can export then delete all records, with no transactions getting in between?
Okay, so I just ended up:
creating a new TEMP table,
selecting everything from the LOG table,
inserting it into the new TEMP table,
then deleting from LOG everything where exists the same record in the TEMP table
exporting the TEMP table
doing a global replace of "INSERT INTO `temp`" into "INSERT INTO `log`"

Combine data across dozens of DB's in a non-expensive query?

I run a site where companies create accounts and have their users sign up for their accounts. A couple of the companies I work with have sensitive data and for that reason the decision was made a while back that we would maintain separate databases for each company that registers with our site.
To be clear, the DB's look similar to the below for companies A, B and C.
db_main /* Stores site info for each company */
db_a
db_b
db_c
Now, I'm finding that sometimes a user creates an account with both company A and company B, so it would be nice if I could combine their progress from the two sites (A and B). For example, if the user earns 5 points on site A, and 5 points on site B, I would like for their total site points to read "10" (their combined total from 5 + 5).
There are hundreds of these databases, though, and I'm worried that it will be rough on the server to be constantly running queries across all databases. The user's score, for instance, is calculated on each page load.
Is there an efficient way to run queries in this manner?
Joining to 100 DB's should never be an option, and to your question, it won't be efficient.
What I would suggest instead is to create a global table that stores a cache of the points you are after globally. Points should not be 'sensitive' in any way from the sounds of it. I assume a userID is not either. Given that a customer should never have direct query access to this table, it should be a non-issue.
Scenario:
User joins siteA
earns 5 points
dbA gets updated
dbGlobalPoints gets upsert'ed (if exists (it won't), update points+5, else insert userID, 5)
User then joins siteB with same username (this may be your biggest issue if you don't have unique id's across systems)
profile query pulls/joins dbGlobalPoints for display
earns 10 points.
dbB gets updated
dbGlobalPoints gets upsert'ed (if exists (it will), update points+10, else insert userID, 10)
On initial run, a 'rebuild' process of sorts will need to be run which steps through each company table and populates the global table. This will also be useful later for a 'recount' process (say, you drop dbA and don't want those points to count anymore)
you could also make this a subroutine that fires per user just once (in the background) if they don't have a record in the global points database.

Storing Historical Data in Separate Fact Tables

I am a newbie when it comes to data warehouse projects and would like to seek the advice of the community here.
I need to create a data warehouse from which both historical and current information can be extracted in the most efficient/inexpensive way possible. In this particular example, we are dealing with web site users and preferences.
We have a Users table, an attribute table called Preferences (with a name of preference and ID) and then a connection fact table called User_Preferences. Rather than storing all history and preferences changes/deletions/additions in the User_Preferences table (which could be 100s of millions of rows), would it make more sense to have 2 tables, one for current preferences and one with all preference history (using an isCurrent flag)? In the ETL process, we would load all historical data from the prod db into one dw table and then insert only those records with an isCurrent=1 in new dw table into another dw table only storing current preferences.
From a business standpoint, the majority of queries would be run on the current data, as customers only care about a user's current preferences. A much smaller number of queries would need to return information about the full history of a user's preferences for the internal interests of the business.
Thanks for any help you can provide!
Yes, it makes sense. I would use a CurrentPreference fact table, as you have it described, and also a TransactionalPreference that report all the changing in preference. From this table you could easily get the history of a user.
A Transactional F.T. (dimension are Time, Transaction, User, Preference) has all the information but is quite difficult to query for past situation (what are the preferences of Texans last year in januar?) so could be useful also a Snapshot Preference, a fact table that contains situation at a point in time (every month, or every day, it depends on your users wish).

How do I keep two article tables synced, but keep stock separate

This is probably a more conceptual problem, but I couldn't find an easy solution.
Scenario: Two shops (say 'M' and 'S']. M is the master and determines the articles in the databases. Each maintains an independent stock. I have M's article table replicating to S, and I separated stock into a separate table with a common reference.
Now when new articles are added in M, they arrive at S too, but they won't have an entry in S's stock table. Similar problem with delete articles. Possible solutions:
Do I create an entry in S's stock table each time a request is made
for a new (not-test-existing) article?
Do I have to scan regularly to check for missing stock entries.
Isn't there a more elegant way to solve this?
NOTE: To clarify, let me explain another way:
M already replicates the 'articles' table to S (using MySQL's replication mechanism.
This works fine.
The problem is that M and S have 'stock' tables which are local to each M and S. What is the normal procedure when, for example, a new product is added (in M) to the 'articles' table, and transferred to S. Now there is new entry which doesn't have a corresponding entry in S's stock table.
I'm guessing this is not an unusual situation - what would be the normal procedure to solve this?
Unless of course if you have two databases located on two different DB servers, why don't you simply create a table articles and a table stock referencing it. You could add the shop (ideally the shop_id) as an extra column of that latter. Something like that:
CREATE TABLE articles(id int primary key not null,
name char(20) not null);
CREATE TABLE stock(article_id int not null,
shop ENUM('M', 'S') not null,
qty int not null,
FOREIGN KEY(article_id) REFERENCES articles(id),
UNIQUE(article_id, shop));
Please see http://sqlfiddle.com/#!2/e6eca/5 for a live example.
If you really need to restrict creation of items on table articles to shop M that could be achieved by creating different users for your DB (*user_m*, *user_s*) and using REVOKE and/or GRANT to setup the various access rights.
EDIT: If the two servers are on distant sites, you would probably be able to use MySQL replication capabilities to keep one or many tables in sync between the two sites. This will require a network access between the two sites. As of myself, for obvious reasons, I would consider using a secure tunnel between the two sites. Finally you still probably have to set-up permissions at DB-level to only allow insert from one site and not the other.
As a "poor's man" solution, you finally have the possibility to backup on regular basis the required tables from one server to update the tables on the second server. A combination of mysqldump + cronjob would be good starting point.
As of myself, I would though push to MySQL replication. The setup is probably more complex. But this will perform better, scale better and have lower latency.