When to put payments in their own SQL table? - mysql

I have a system where users posts data and they can upgrade their post by optionioally paying to upgrade. This is the information I want to store from stripe on their payment response:
CREATE TABLE IF NOT EXISTS `db`.`pay` (
`payments_id` int(11) NOT NULL AUTO_INCREMENT
payment, unique index',
`stripe_id`
`card_id`
`brand`
`amount`
`created`
`currency`
`paid`
`refunded`
`exp_month`
`exp_year`
`last4`
`country`
`fingerprint`
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='payments';
Should this be in the same table as the one containing the main post data, or should it be a separate and linked table. What logic is used to make this decision?
On one hand it seems nice to separate it but then you also have the overhead of linking the tables. Only one payment will ever be associated with one post.

Card data should be in a different table with a userId that links to your user table.
With limited knowledge of what you're trying to achieve I would say you need at least 3 tables.
User Table
Post Table with userId linking back to user table
Payment Card Table with userId linking back to user table

Related

What is the most efficient way to check against a huge MySQL table?

I have a service in which users may "like" content posted by other users. Currently, the system doesn't filter out content that the user has already liked, which is undesirable behavior. I have a table called LikeRecords which stores a userID, a contentID, and a timePlaced timestamp. The idea is to use this table to filter content that a user has already liked when choosing what to display.
The thing is, I'm a MySQL amateur, and don't understand scaling and maintenance well. Even though I only have about 1,500 users, this table already has 45,000 records. I'm worried that as my service grows to tens or hundreds of thousands of users, this table will explode into millions and become slow since the filter operation would be called very frequently.
Is there a better design pattern I could use here, or a maintenance technique I should use?
EDIT: Here is the query for building the table in question:
CREATE TABLE `likerecords` (
`likeID` int(11) NOT NULL AUTO_INCREMENT,
`userID` int(10) unsigned NOT NULL,
`orderID` int(11) NOT NULL,
`timePlaced` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`special` tinyint(1) NOT NULL,
PRIMARY KEY (`likeID`)
) ENGINE=InnoDB AUTO_INCREMENT=44775 DEFAULT CHARSET=latin1
I would be using it to filter results in other tables, such as an "orders" table.

Relationship with two table

I'm new to PHP, and I am working on a project for a friend of mine. He needs a system where members can register and upload images.
I have done all of that so far but my main problem is members can only upload a single image to the database seen where the image row is in the table as the registration information.
For example, if you click a single user you'll be able to see all the images which he/she uploaded to the site.
How do I create a table for a user's images and have it interact with the user's information table?
You need to add a new table into the system, which will hold multiple rows for same member_id (1 to many relationship).
For example:
CREATE TABLE `member_images` (
`image_id` BIGINT(20) UNSIGNED NOT NULL auto_increment,
`member_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
`image_path` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY(`image_id`),
KEY (`member_id`)
);
Here PK (primary key) is on a column different from member_id, so this table can hold multiple entries for the same member.

Best way with relation tables

I have a question about tables and relations tables ...
Actually, I have these 3 tables
CREATE TABLE USER (
ID int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(14) DEFAULT NULL
);
CREATE TABLE COUNTRY (
ID int(11) NOT NULL AUTO_INCREMENT,
COUNTRY_NAME varchar(14) DEFAULT NULL
);
CREATE TABLE USER_COUNTRY_REL (
ID int(11) NOT NULL AUTO_INCREMENT,
ID_USER int(11) NOT NULL,
ID_COUNTRY int(11) NOT NULL,
);
Ok, so now, 1 user can have one or more country, so, several entries in the table USER_COUNTRY_REL for ONE user.
But, my table USER contains almost 130.000 entries ...
Even for 1 country by user, it's almost 10Mo for the USER_COUNTRY_REL table.
And I have several related tables in this style ...
My question is, is it the fastest, better way to do?
This would not be better to put directly in the USER table, COUNTRY field that contains the different ID (like this: "2, 6, ...")?
Thanks guys ;)
The way you have it is the most optimal as far as time constraints go. Sure, it takes up more space, but that's part of space-time tradeoff - If you want to be faster, you use more space; if you want to use less space, it will run slower (on average).
Also, think of the future. Right now, you're probably selecting the countries for each user, but just wait. Thanks to the magic of scope creep, your application will one day need to select all the users in a given country, at which point scanning each user's "COUNTRY" field to find matches will be incredibly slow, as opposed to just going backwards through the USER_COUNTRY_REL table like you could do now.
In general, for a 1-to-1 or 1-to-many correlation, you can link by foreign key. For a many-to-many correlation, you want to have a relation table in between the two. This scenario is a many-to-many relationship, as each user has multiple countries, and each country has multiple users.
Why not try like this: Create table country first
CREATE TABLE COUNTRY (
CID int(11) NOT NULL AUTO_INCREMENT,
COUNTRY_NAME varchar(14) DEFAULT NULL
);
Then the table user:
CREATE TABLE USER (
ID int(11) NOT NULL AUTO_INCREMENT,
NAME varchar(14) DEFAULT NULL,
CID Foreign Key References CID inCountry
);
just Create a Foreign Key relation between them.
If you try to put this as explicit relation , there will lot of redundancy data.
This is the better approach. You can also make that Foreign Key as index . So that the databse retrieval becomes fast during search operations.
hope this helps..
Note : Not sure about the exact syntax of the foreign key

Database Edit with Approval - mysql

I currently have a database which many users can access and make changes to. The is also a log database that stores all changes to tables within the database using triggers.
I would like to add the ability to approve edits before they are changed in the database.
What would be the best way to go about this?
We have something similar on one of our sites, we've added a bunch of tables:
users sites ... etc
Then we have a bunch of shadow tables:
users-shadow sites-shadow ... etc
The shadow tables have identical structures to the real tables except for an added line for the user who made the change. So first we use this query when a change is submitted by a user who needs to have his/her database actions approved:
REPLACE INTO users-shadow (user_mod,id,username,password,salt...) VALUES (16,50,'bob','stuff','salt'...);
Obviously, make sure this isn't open to injection, use prepared statements etc.
When approved, a row in the shadow table is simply removed from the shadow table, the user_mod value dropped and changes (non-null values) inserted into the real table (or updated if an id is specified, using REPLACE syntax). We do this logic in perl so sadly don't have any SQL on hand for it.
Remember that SQL REPLACE does a DELETE and an INSERT rather than an UPDATE. You will need to amend any triggers to allow for this behaviour.
Note: The reason we didn't use an 'approve' flag was that we needed the ability to amend existing records, of course we couldn't have multiple records with the same primary key.
well i have made this system once and here is my solution for DB structure and over all algorithm:
there should be a sub system of admin panel which different users can manage their products but every change should be approved by administrator before going affecting the main Product table. there is three main table:
1.Product : store products that have final approved and are used in entire system
2.Changes_versions : a table with One To Many Relation with Product Table that indicates each change version is committed by who , when ,and is approved/rejected by admin or still is in Pending state .table structure is as following :
CREATE TABLE changes_versions(
xid int(11) unsigned NOT NULL AUTO_INCREMENT,
xcreated_date datetime DEFAULT NULL,
xupdated_date timestamp NULL DEFAULT NULL,
xversion int(11) DEFAULT NULL,
xobject_id int(11) DEFAULT NULL,
xobject_type varchar(255) DEFAULT NULL,
xstate enum('PENDING','ACCEPTED','REJECTED') DEFAULT 'PENDING',
PRIMARY KEY (xid)
) ENGINE=InnoDB AUTO_INCREMENT=165 DEFAULT CHARSET=utf8
3.Changes : a table that have One To Many relation with Changes_versions table that keep every column change record of the main Table (here i mean product table) and by approving a change_version record by admin its related changes records will be placed in main table column. table structure is as following :
CREATE TABLE changes(
xid int(11) unsigned NOT NULL AUTO_INCREMENT,
xcreated_date datetime DEFAULT NULL,
xcreated_by varchar(255) DEFAULT NULL,
xupdated_date timestamp NULL DEFAULT NULL,
xupdated_by varchar(255) DEFAULT NULL,
xversion_id int(11) DEFAULT NULL,
xcolumn_name varchar(255) DEFAULT NULL,
xcolumn_value varchar(255) DEFAULT NULL,
xstate enum('PENDING','ACCEPTED','REJECTED') DEFAULT 'PENDING',
xadmin_review text,
PRIMARY KEY (xid)
) ENGINE=InnoDB AUTO_INCREMENT=764 DEFAULT CHARSET=utf8
with this system and table schema i handled to work with record changes, user fetch list of records ,if user have any Pending state change_version, system will pull its related changes records and place them in the right column in the fetched product row(temporary just for displaying) , so even if user has any pending state changes he/she can see its changes in his/her panel(not main system, only his/her panel).
at the end if system administrator accept a user changes_version version and its related changes records ,system should place each changes table record in the right column of product table(for example i used product table, with this system you can versioning and admin approving any table).and change version record state to approved and its changes related records to approved to. so with this structure you can save and versioning different tables and keep log of each version changes.

In MySQL, how to "join" three tables

I've seen a good amount of threads on "how to join more than two tables" but none of those threads seem to solve my problem.
I have three tables:
teams, persons and payfiles
teams Table looks like this:
DROP TABLE IF EXISTS `teams`;
CREATE TABLE IF NOT EXISTS `teams` (
`team_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`leader` varchar(32) NOT NULL,
PRIMARY KEY (`team_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=30;`
persons table:
DROP TABLE IF EXISTS `persons`;
CREATE TABLE IF NOT EXISTS `persons` (
`team_id` int(2) DEFAULT '0',
`hash` varchar(32) NOT NULL,
UNIQUE KEY `hash` (`hash`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
payfiles table:
DROP TABLE IF EXISTS `payfiles`;
CREATE TABLE IF NOT EXISTS `payfiles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`hash` varchar(32) NOT NULL,
`deals_passed` int(2) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1298 ;
Tables have much more columns, but I trimmed them for the sake of simplicity.
teams table contains records in the following way:
1,Team 1,afaf7878af78a
The latter is the team leader's unique hash.
The table persons contains all the personal information, and a unique hash,
For instance
John Doe whose hash is afaf7878af78a who also is the leader of Team 1.
The table payfile also has a "hash" column, that table contains all the information needed to compute employees' checks.
The management team want a general vision of how much the company is making. They want to see how much money every single team is bringing in.
The problem I'm facing here, is trying to group the earnings by "teams"
My best try so far is this
SELECT hash, SUM(deals_passed) as deals FROM payfiles JOIN persons ON persons.hash = payfiles.hash GROUP BY payfiles.hash
but I can't see an optimized way to query the database in order to generate a team by team general vision of earnings without changing the structure of the database.
For instance:
John Doe, and Jane Doe belong to "Team 1" and they brought in $500 and $600 respectively.
I want to generate something like:
"Team 1 brought in $1100"
My alternative is to change the structure of the database, and add a new column to the payfile table. Such column would be team_id so I can query it easily, but the database currently has about 10,000 records so that implies updating the 10K records that didn't consider a team_id column, and make a lot of changes to the GUI, something that I don't really want to do, although if that's the easiest and best option I'll do it.
Thanks!
SELECT
teams.name,
SUM(payfiles.deals_passed) AS team_deals_passed
FROM payfiles
LEFT JOIN persons USING (hash)
LEFT JOIN teams USING (team_id)
GROUP BY teams.team_id
You can use SUM() to the get the total, and use GROUP BY for the team to get each total by team.