Update query using select -- how does it know which row to update - mysql

I have the following query that is working correctly but I don't understand why. I am changing the balance column for each row..How does it know which row to update for a particular customer.
UPDATE phppos_customers SET balance =
IFNULL((SELECT SUM(transaction_amount)
FROM `phppos_store_accounts`
WHERE deleted = 0 and customer_id = phppos_customers.person_id), 0);

For the sake of example, lets say you have the following two tables
[Users]
+---------+----------+
| user_id | username |
+---------+----------+
| 1 | patrick |
| 2 | chris |
+---------+----------+
[Names]
+---------+------------+-----------+
| user_id | first_name | last_name |
+---------+------------+-----------+
| 1 | Patrick | Stewart |
| 2 | Chris | Angel |
+---------+------------+-----------+
If you had a update query like the one in your original post, you would want to tell it how to align the two tables. If you had the clause WHERE Users.user_id = Names.user_id, you are effectively telling SQL to view the data as if both tables were aligned side by side, using the user_id in both tables to determine where they match up. This would mean the first_name and last_name in the [Names] table for user_id 1 will be what is used when updating the row in the [Users] table that is user_id 1. It is essentially viewing the data merged together, like this:
[Users] and [Names] tables aligned by the user_id columns
+---------+----------+---------+------------+-----------+
| user_id | username | user_id | first_name | last_name |
+---------+----------+---------+------------+-----------+
| 1 | patrick | 1 | Patrick | Stewart |
| 2 | chris | 2 | Chris | Angel |
+---------+----------+---------+------------+-----------+
So when SQL is doing the updating, it updates each row with the corresponding data from the other table, using this aligning to know which data to use for each rows update.
The process of aligning/merging data from multiple tables is called joining in SQL, here is some more information that illustrates how it works if you are interested.

Related

Mysql - Compare int field with comma separated field from another table

I have two tables in a MySQL database like this:
User:
userid |userid | Username | Plan(VARCHAR) | Status |
-----------+------------+--------------+---------------+---------+
1 | 1 | John | 1,2,3 |1 |
2 | 2 | Cynthia | 1,2 |1 |
3 | 3 | Charles | 2,3,4 |1 |
Plan: (planid is primary key)
planid(INT) | Plan_Name | Cost | status |
-------------+----------------+----------+--------------+
1 | Tamil Pack | 100 | ACTIVE |
2 | English Pack | 100 | ACTIVE |
3 | SportsPack | 100 | ACTIVE |
4 | KidsPack | 100 | ACTIVE |
OUTPUT
id |userid | Username | Plan | Planname |
---+-------+----------+------------+-------------------------------------+
1 | 1 | John | 1,2,3 |Tamil Pack,English Pack,SportsPack |
2 | 2 | Cynthia | 1,2 |Tamil Pack,English Pack |
3 | 3 | Charles | 2,3,4 |English Pack,Sportspack, Kidspack |
Since plan id in Plan table is integer and the user can hold many plans, its stored as comma separated as varchar, so when i try with IN condition its not working.
SELECT * FROM plan WHERE find_in_set(plan_id,(select user.planid from user where user.userid=1))
This get me the 3 rows from plan table but i want the desired output as above.
How to do that.? any help Please
A rewrite off your query what should work is as follows..
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
Plan
WHERE
FIND_IN_SET(Plan.plan_id,(
SELECT
User.Plan
FROM
user
WHERE User.userid = 1
)
)
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
You also can use FIND_IN_SET on the ON clause off a INNER JOIN.
One problem is that the join won't ever use indexes.
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
User
INNER JOIN
Plan
ON
FIND_IN_SET(Plan.id, User.Plan)
WHERE
User.id = 1
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
Like i said in the comments you should normalize the table structures and add the table User_Plan whats holds the relations between the table User and Plan.

mySQL JOIN statement with COUNT not matching correctly

I am trying to write a mySQL statement that selects data from one table but counts up entries from another table with a matching ID in a specific field.
The two tables are jobs and job_cards. A job will always be a single entry which will have multiple job cards, so I need to write a singular statement that selects data from the job table but adds another field in the result which is a count of all related job cards.
Example:
jobs table:
| ID | customer | status | date_added |
|----------------------------------------|
| 1 | 3 | active | 2017-10-10 |
------------------------------------------
job_cards table is a bit more complex but includes a column called job_id which will be 1 in this case. But lets say there are 3 cards assigned to the job above. I wrote the following statement:
SELECT j.*, COUNT(jc.id) AS card_count FROM jobs j LEFT JOIN job_cards jc ON j.id = jc.job_id
But the count column only returns the TOTAL number of cards in the job_cards table, regardless of which job they are assigned to. Not only that, but it only ever returns a single result even though at the moment there are 4 entries in the jobs table.
Is there any way to do what I need to do with a single statement?
EDIT:
Sample data from the job_cards table:
| ID | job_id | customer | description | materials | notes |
|--------------------------------------------------------------|
| 1 | 1 | 3 | blah blah | none | test |
| 2 | 1 | 3 | something | pipes | n/a |
----------------------------------------------------------------
The result I would like to get is:
| ID | customer | date_added | card_count |
|-------------------------------------------|
| 1 | 3 | 2017-10-10 | 2 |
---------------------------------------------
Where the ID here is the ID of the job.
You can try this:
SELECT *, (select count(*)
from job_cards jc
where jc.job_id=j.id) as card_count
FROM jobs j

Mysql: Change set column on update condition?

I'm trying to figure out the best way to update one of two fields in a table. I have a personrelationship table and it links two people and I would like to have each person be able to set the relationship type on their end.
PersonRelationship Table
id int
user1_id int
user2_id int
user1_reltype /* boss, manager, etc */
user2_reltype
Depending on whether the current user is either user1_id or user2_id in the table, I need to update the user_reltype accordingly. So basically if current userid is in the user1_id field then update user1_reltype otherwise update the user2_reltype.
Since you want each user to be able to independently manage their half of the relationship, you can simplify your table structure
--------------------------------------
| initiator_id | reltype | target_id |
When a person with ID 5 (the 'initiator') marks person with ID 9 (the 'target') as a friend, the table will contain:
---------------------------------------
| initiator_id | reltype | target_id |
+--------------+----------+-----------+
| 5 | 'friend' | 9 |
If person 9 later initiates a 'boss' connection with person 5, the entry can be created without interfering with the row previously created by person 5:
--------------------------------------
| initiator_id | reltype | target_id |
+--------------+---------+_----------+
| 9 | 'boss' | 5 |
This approach will make your table easy to read and your queries easy to write.
Extra:
If you do not already have it, consider creating another table to track relationship types ('reltype'):
-----------------
| id | type |
+----+----------+
| 1 | 'friend' |
| 2 | 'boss' |
and replace the string reltype's in the relationship table with foreign keys.
---------------------------------------
| initiator_id | reltype | target_id |
+--------------+----------+-----------+
| 5 | 1 | 9 |
| 9 | 2 | 5 |

SQL Trigger Multiple Tables

I want to trigger an Update on multiple sql tables without creating a loop.
Lets say I have 2 tables:
Table: User_Names
---------------
|Name | Clark |
|Gen | Male |
|id | 1 |
---------------
Table: User_Ages
---------------
|Age | 34|
|Gen | Male |
|id | 1 |
---------------
The id's are unique and refer to the same person.I want to update the columnGen in User_Names, my trigger should update it in the other Table. I also want this to happen when I change it in User_Ages Table, But if both update eachother im creating a loop on the Update trigger in mysql. How do I prevent this loop? The point here is creating a SQL Trigger.
I'm not going to address your original question given the nature of your example. This is a normalization issue much more than trigger issue.
In this case you should normalize your data and only store it in one place. Example above also suggests that you have slight misunderstanding on how to use rows and columns.
Given the example, better layout would probably be:
Table: User_names
+----+---------+------+
| id | Name | gen |
+----+---------+------+
| 1 | Clark | Male |
+----+---------+------+
Table: User_Ages
+----+------+
| id | age |
+----+------+
| 1 | 34 |
+----+------+
When you want to retrieve both values, you'd just link them in your query, e.g.
SELECT user_names.id,name,gen,age FROM User_names JOIN User_Ages USING (id);
Would give you:
+----+---------+------+-----+
| id | Name | gen | age |
+----+---------+------+-----+
| 1 | Clark | Male | 34 |
+----+---------+------+-----+
Coming back to your original question: In situation like that I'd question the original design. If it is really called for, then I'd pick one table that acts as a master and propagates the changes to other table. E.g. define the trigger on User_names table and use it to populate User_Ages table as well.

MySQL Multi Duplicate Record Merging

A previous DBA managed a non relational table with 2.4M entries, all with unique ID's. However, there are duplicate records with different data in each record for example:
+---------+---------+--------------+----------------------+-------------+
| id | Name | Address | Phone | Email | LastVisited |
+---------+---------+--------------+---------+------------+-------------+
| 1 | bob | 12 Some Road | 02456 | | |
| 2 | bobby | | 02456 | bob#domain | |
| 3 | bob | 12 Some Rd | 02456 | | 2010-07-13 |
| 4 | sir bob | | 02456 | | |
| 5 | bob | 12SomeRoad | 02456 | | |
| 6 | mr bob | | 02456 | | |
| 7 | robert | | 02456 | | |
+---------+---------+--------------+---------+------------+-------------+
This isnt the exact table - the real table has 32 columns - this is just to illustrate
I know how to identify the duplicates, in this case i'm using the phone number. I've extracted the duplicates into a seperate table - there's 730k entires in total.
What would be the most efficient way of merging these records (and flagging the un-needed records for deletion)?
I've looked at using UPDATE with INNER JOIN's, but there are several WHERE clauses needed, because i want to update the first record with data from subsequent records, where that subsequent record has additional data the former record does not.
I've looked at third party software such as Fuzzy Dups, but i'd like a pure MySQL option if possible
The end goal then is that i'd be left with something like:
+---------+---------+--------------+----------------------+-------------+
| id | Name | Address | Phone | Email | LastVisited |
+---------+---------+--------------+---------+------------+-------------+
| 1 | bob | 12 Some Road | 02456 | bob#domain | 2010-07-13 |
+---------+---------+--------------+---------+------------+-------------+
Should i be looking at looping in a stored procedure / function or is there some real easy thing i've missed?
U have to create a PROCEDURE, but before that
create ur own temp_table like :
Insert into temp_table(column1, column2,....) values (select column1, column2... from myTable GROUP BY phoneNumber)
U have to create the above mentioned physical table so that u can run a cursor on it.
create PROCEDURE myPROC
{
create a cursor on temp::
fetch the phoneNumber and id of the current row from the temp_table to the local variable(L_id, L_phoneNum).
And here too u need to create a new similar_tempTable which will contain the values as
Insert into similar_tempTable(column1, column2,....) values (Select column1, column2,.... from myTable where phoneNumber=L_phoneNumber)
The next step is to extract the values of each column u want from similar_tempTable and update into the the row of myTable where id=L_id and delete the rest duplicate rows from myTable.
And one more thing, truncate the similar_tempTable after every iteration of the cursor...
Hope this will help u...