UPDATE multiple MySQL rows with different values - php

Note: I realize this may be confusing taking about tables and columns below so here is a simplified version of the two tables I mention:
Table "categories" has columns: id, type
Table "entries" has columns: id, categories, ...
I have a MySQL table named entries where one of the columns, named categories, is a string of pipe separated indices. For example, "|1|2|3|" might be a possible value and "|1|3|4|" could be another. The numbers represent indices of the table categories.
I'm writing an admin script that allows a new row to be inserted into the categories table and when doing this, the appropriate rows of the table entires should have the categories column updated. For example, if a newly inserted row of the categories table has index 5 then "5|" should be concatenated to each appropriate column categories of the entries table.
I realize that I could could use UPDATE per appropriate entries row when adding a new category but am wondering if there is a better way to go about this. In case this is not clear I know that this is an option but want to know if this can be made into one statement (pseudo-code):
foreach ($entriesToUpdate as $currEntry)
"UPDATE entires SET categories='".$currValue."|".$newIndex."' WHERE id=".$currId;

This can be done with an expression-based update:
UPDATE entries SET categories=CONCAT(categories, "5|") WHERE id IN (1,2,3,4,...)
(5| instead of 5| from your example, since your examples seem to show that the existing value will start and end with |s already.)
That said, you'd probably be better off with a database schema that stores a mapping of entries to categories in a separate many-to-many table. For example:
categories:
id | type | ...
---------------
1 ...
2 ...
3 ...
entries:
id | ...
---------
100 ...
101 ...
102 ...
entries_to_categories:
entry | category
----------------
100 1
100 2
101 3
Then you can use JOINs to retrieve the set of categories if desired, or check if something is in a category. In this case, entry 100 is in categories 1 and 2, entry 101 is in category 3, and entry 102 is in no categories.

Related

Specific SQL issue: How to select rows where ALL IDs match... (MySQL)

I have this very specific problem which I can't even decide how to approach. So I have 3 tables in MySQL.
Table recipe: id_recipe| name | text | picture
Table ingredients_recipe: id_rs | id_recipe| id_ingredients
Table ingredients: id_ingredient | name | picutre
This is a site, where you select ingredients(so the input is 1 or more id_ingredient) and it should display three categories:
All recipes you can make right now (you have all the ingredients required for it)
All recipes where you are missing only 1 or 2 ingredients
All recipes where you are missing only 3 or 4 ingredients.
Can you help me with these 3 SQL selects? I'm pretty deadlocked right now. Thanks.
SAMPLE DATA: http://pastebin.com/aTC5kQJi
I think your basic statement is already on the right track. You just need to do a little trick. You cannot compare them directly, but you can compare the count of ingredients:
SELECT id_receipe, count(id_rs) as ingredient_count
FROM ingredients_recipe
WHERE id_ingredient IN ( 2, 5)
GROUP BY id_recipe
This will give you the count of ingredients you have for each receipe. Now get the total amount of ingredients for each receipe
SELECT id_receipe, count(id_rs) as ingredient_count
FROM ingredients_recipe
GROUP BY id_recipe
an compare them. Taking the first query as a basis. You can easily get your three different categories out of this.

Joining 3 tables was 1 table performance MYSQL

in a school management system, we need to incorporate 3 semester grades for each subject by each student.
after a discussion, i came up with two solutions.
solution 1
create 3 tables for each semester. (gradeSemester1, gradeSemester2, gradeSemester3)
solution 2
create 1 table called, semesterGrades and with a type handle all 3 semesters.
the reason for solution 1 is to stop data duplication. for example, if there is 8 subjects for a student. this can only contain 8 records in a table where as in solution 2 it can contain up to 24 records of a single student.
what will be the best solution when performance is a major concern ? why solution 2 is better ?
You don't want to change the Database structure depending on the data, so creating a new table whenever you need a new semester is bad design.
All you need is one extra table to store the grades. However I would personally not store them in columns, but in rows to be more flexible (maybe some day you want more than the grades of only 3 semesters).
The table would look like this:
ID | StudentID | SemesterID | SubjectID | Grade
Another advantage of this approach is that you know which semester a grade belongs to. If you have 3 columns for the grades, you only know the grades but you have no information about the semester (I'm guessing he could take more than 3 semesters if needed).
Also I would not worry about performance with this approach. You will have to join tables together but with the proper indexes set up that should not be an issue.
Joining tables is way more costly than a single select because in a join you're selecting and THEN pairing to create a single huge table.
Why not Solution 3:
Create 1 table with 3 columns (one for each semester). That's effectively what your join will be doing each time anyway. Is there any reason to keep them separate?
EDIT (explanation):
Unless I'm misunderstanding something...
A single row in this table would relate a student to a subject and could contain three columns (one for each semester grade). Assuming you have a table for students AND another table for subjects. Containing three semester grades in this table would still be normalized.
TABLE
----------------------
student_id
subject_id
semester1grade
semester2grade
semester3grade

Populating a single-dimensional array with multiple MySQL column values

I am quite new to PHP and MySQL, but have experience of VBA and C++. In short, I am trying to count the occurrences of a value (text string), which can appear in 11 columns in my table.
I think I will need to populate a single-dimensional array from this table, but the table has 14 columns (named 'player1' to 'player14'). I want each of these 'players' to be entered into the one-dimensional array (if not NULL), before proceeding to the next row.
I know there is the SELECT DISTINCT statement in MySQL, but can I use this to count distinct occurrences across 14 columns?
For background, I am building a football results database, where player1 to player14 are the starting 11 (and 3 subs), and my PHP code will count the number of times a player has made an appearance.
Thanks for all your help!
Matt.
Rethink your database schema. Try this:
Table players:
player_id
name
Table games:
game_id
Table appearances:
appearance_id
player_id
game_id
This reduces the amount of duplicate data. Read up on normalization. It allows you to do a simple select count(*) from appearances inner join players on player_id where name='Joe Schmoe'
First of all, the database schema you're using is terrible, and you just found out a reason why.
That being said, I see no other way then to first get a list of all players by distinctly selecting the names of players into an array. Before each insertion, you would have to check if the name is already in the array (if it is already in, don't add it again).
Then, when you have the list of names, you would have to run an SQL statement for each player, adding up the number of occurences, like so:
SELECT COUNT(*)
FROM <Table>
WHERE player1=? OR player2=? OR player3=? OR ... OR player14 = ?
That is all pretty complicated, and as I said, you should really change your database schema.
This sounds like a job for fetch_assoc (http://php.net/manual/de/mysqli-result.fetch-assoc.php).
If you use mysqli, you would get each row as an associative array.
On the other hand the table design seems a bit flawed, as suggested before.
If you had on table team with team name and what not and one table player with player names.
TEAM
| id | name | founded | foo |
PLAYER
| id | team_id | name | bar |
With that structure you could add 14 players, which point at the same team and by joining the two tables, extract the players that match your search.

Searching depending on the category with filters

What is the best practice to do a searching form with filters, where the filters are depending on the category?
e.g.:If you go to visit the ebay and select a category then the filters are different on the left hand side in the cell phones (filters: brand, operating system...) or the fashion category (filters: size, color...)...
In my mind I would do more table in DB. Each table for one category (cat_cellphone, cat_fashion...). Then put a product one of these tables depending on the category (not one products table where one column contains the category ID).
These tables are different where the columns names are characterized by the category.
Next, should do more searching forms and call a form where the filter belongs to the category.
Is it a good concept or there is an other accepted practice in big projects?
No, having multiple tables is a bad idea.
In general. use a table with a primary key over two columns instead.
(The primary key may span more than one column.)
The columns would be categoryname / filtername.
If you don't like such primary keys, and always use an autoincrement column, you can still create an index over the two columns.
The columns would be: id / categoryname / filtername / filtertext
Using multiple tables to store your products isn't a good idea. Because there will be products that overlap in categories which would result storing the same product in more than one table.
Just use a product table with the ID, product_number, description, etc. and a category table to store the different categories. Then you could link them directly like:
Product table:
ID product_number description category
1 00001 Screwdriver 1
Category table:
ID description
1 Tools
And you could even expand the category table with an extra column to use subcategories by addressing the parent of the subcategory:
Category table with subcategories:
ID description parent
1 Tools NULL
2 Automatic tools 1
And if you don't like directly linking to the category table from the product table you could use a link table:
Product_category:
Product_ID Category_ID
1 1
Hope this answers your question.
Edit, added filter table:
To add filtering for a product you can use a table for a the filters and a link table, like so:
Filter:
Filter_ID description value
1 brand Bosch
2 brand Bahco
3 type Phillips
Product_Filter:
Product_ID Filter_ID
1 1
1 3
That way you can link more than one filter to each product and use the same filter more than once.
You could even expand this further by using another table for the filter values, but that could make things a bit to complicated:
Filter:
Filter_ID description value
1 brand 1
Filter_value:
Filter_ID value
1 Bosch
2 Bahco

PHP select row from db where ID is found in group of IDs

I have 3 employers IDs: 1, 2 and 3. I am generating tasks for each one by adding a line in database and in column "for_emp" I insert IDs I want to assign this task for and could be all 3 of them separated by comma. So let's say I got a task and "for_emp" is "1,2,3", the employers IDs. If I would like to select all tasks for the ID 2, will I be able to select from the row that has "1,2,3" as IDs and just match "2" there ? If not, how do you suggest I insert my emp IDs into one row in database ? The db is MySQL.
Any ideas ? Thanks.
Don't do it like that, you should normalize your database.
What you want to do is have a table such as task, and then task_assignee. task_assignee would have fields task_id and user_id. If a task has eg. three assignees (IDs 1, 2 and 3), then you'll create three rows in the task_assignee table for that one task, like this:
+--------+---------+
|task_id | user_id |
+--------+---------+
| 1 | 1 [
| 1 | 2 [
| 1 | 3 [
+--------+---------+
Then it's just a simple matter of querying the task_assignee table to find all tasks that are assigned to a given user.
Here's an example of how to get all the tasks for user_id 2:
SELECT t.* FROM task AS t INNER JOIN task_assignee AS ta WHERE ta.user_id = 2
EDIT.
Just as a related note, even if you didn't do it the right way (which I described in my answer previously), doing it with hacks such as LIKE would still be far from the optimal solution. If you did store a list of comma-separated values, and needed to check if eg. the value 2 is in the list, you could use the MySQL's FIND_IN_SET function:
SELECT * FROM task WHERE FIND_IN_SET(2, for_emp)
But you shouldn't do this unless you have no choice (eg. you're working with someone's shitty DB design), because it's way more inefficient and won't let you index the the employee ID.
The following query should do what you want:
SELECT * FROM tasks WHERE for_emp LIKE '%2%';
However, be aware that that would also match employers 12, 20, 21 etc; so take care if you expect you might end up in double-digits.
However, the other answers about renormalising your database are definitely preferable.
You're doing it wrong. Create a relation table with two fields: employee id and task id. If one task should be assigned to three employees, insert three rows in the relation table.
You then use JOIN to join the task, employee and relation tables.
then its no proper relation...
I would suggest a "mapping table" for the n:m relation
employee
id
task
id
employeetask
task_id
employee_id
Make a table for your employers. Insert your three rows in it.
Then make a table for mapping tasks to employers. If a task is assigned to three employers, insert three rows into this table. This is basic entity-relation work.
I would make 2 different tables.
1 with employees, and 1 with tasks.
then make another table which combines the two tables, I will call it Assigned Tasks.
Then in assigned tasks I make a assigned id, a employeenumber which is a FK to the employee table and a taskid which is a FK to the Tasks table.
If an employee has more than 1 task. Just insert another row in the assigned table. ;)
When its about Databases, try to think in solo entities! Combining those entities is able in antoher table.
sql example:
Select * from Assignedtasks where employeeID = 1 will give you all his/her tasks. :)
You could use a LIKE '%,2,%' clause in your SELECT statement.
eg:
SELECT * FROM table where for_emp LIKE '%,2,%'
However performance of such non-sargable queries is usually quite bad.
I would suggest that you insert a row each for each employee who is assigned to the task using a separate TASK_EMPLOYEE_MAPPING table with taskId, employeeId as a composite primary key.
With such a design, your query will be
SELECT * FROM TASK_EMPLOYEE_MAPPING WHERE employeeId = '2'

Categories