Create new records from a field with comma delimited values - mysql

I have a poorly created table I want to update. It is set up as
ID
Name
Value
Because a given Name can have more than one value right now the Value field is varchar and populated with comma delimited values:
12,15,92
I would like to create an update or create table query that will make those into separate records so a table with
ID | Name | Value
1 | Bob | 5,6,9
2 | Alice| 5,9
3 | Ted | 1
ends up as
1 | Bob | 5
2 | Bob | 6
3 | Bob | 9
4 | Alice | 5
5 | Alice | 9
6 | Ted | 1

In searching online it appear this is a pretty common issue and I found one of several functions for splitting delimited fields into records here:
http://kedar.nitty-witty.com/blog/mysql-stored-procedure-split-delimited-string-into-rows

This is an old question but there is a mysql function for this, it name is group_concat, you can see the function in the offical documentation, also you can see an example here

Related

Laravel get "unique" row from mysql

I have a special scenario to fetch "unique" row.
Let's say the database is like below
| id | userid | value | others |
|----|--------|-------|---------|
| 1 | 111 | 10 | string1 |
| 2 | 112 | 30 | string2 |
| 3 | 112 | 30 | string3 |
| 4 | 113 | 50 | string4 |
what I want to achieve is to fetch the unique rows based on the "userid" so I'am able to sum all values.
the expect output row can be either id: 1 2 4 or 1 3 4 (both is acceptable for this special case because same id guarantees same value, or in general, get just one row from those row with same userid. ), so the sum will be 90.
Note: DB is extended from Eloquent\model
My old approach is to get DB::unique('userid'); then for each userid DB::where('userid', $id)->value('value'), add the result to sum; I just believe there might be a better approach.
There is Illuminate\Support\Facades\DB in Laravel, it can return the Query Builder. Not recommend to use a model that is named DB.
So just change another name.
By the way, for Eloquent\Model, you can use groupBy and sum too:
Model::groupBy('user_id')->sum('value');

how to use where caluse on primary key? [duplicate]

This question already has an answer here:
Query Distinct values from a multi-valued column
(1 answer)
Closed 5 years ago.
i'm developing a quiz website. In my database, I need a table which shows
reported quiz errors. It should look like this:
______________________________________________________________________
|key| quiz_number | who_reported_this_error | reported_number |
-----------------------------------------------------------------------
| 1 | 5 | goid482,saiai10,hahakaka | 3 |
-----------------------------------------------------------------------
| 2 | 3 | fiiai55,kihogi84 | 1 |
-----------------------------------------------------------------------
If a user named hanabi reported an error about quiz number 5,
first I need to check the who_reported_this_error column because
I don't want for a user to report same error twice. If the user 'hanabi' doesn't exist in "who_reported_this_error" column I should update row 1.
Now for my problem. I want to find a row which I should update with a key column, and the key column's number should automatically increased. But I know that I can't use a WHERE clause on this primary key. Hhow can I solve this problem?
The problem is with the table schema. NEVER store comma-separated data in a single column. You should structure the table to look more like this:
____________________________________________
|key| quiz_number | who_reported_this_error |
────────────────────────────────────────────
| 1 | 5 | goid482 |
---------------------------------------------
| 2 | 3 | fiiai55 |
---------------------------------------------
| 3 | 5 | saiai10 |
---------------------------------------------
| 4 | 5 | hahakaka |
---------------------------------------------
| 5 | 3 | kihogi84 |
--------------------------------------------
You might also want a timestamp column on this table. Then, put a UNIQUE constraint on the quiz_number and who_reported_this_error columns to prevent the duplicates.
If you later need to see everyone who reported errors for quiz 5 in the same record, use MySql's group_concat() function to build that information on the fly. Just don't store the data that way.
The key column has nothing to do with this question. You certainly can use your primary key in a WHERE clause. It just won't help you in this case because that data isn't relevant to the problem at hand.

MySQL combine columns before matching it with LIKE

I have two columns in my database that I want to combine before matching them using LIKE statement.
My table:
|---------------------------------|
| ID | PREFIX | SUFFIX |
|---------------------------------|
| 1 | 31 | 523 |
|---------------------------------|
| 2 | 62 | 364 |
|---------------------------------|
I want to be able to supply 315 and ID 1 would be returned. Is there any easy way of doing it? At the moment I am splitting search string and matching separate columns.
Thanks.
SELECT * FROM table WHERE CONCAT(PREFIX, SUFFIX) LIKE '%315%'

How to store multiple values in single column where use less memory?

I have a table of users where 1 column stores user's "roles".
We can assign multiple roles to particular user.
Then I want to store role IDs in the "roles" column.
But how can I store multiple values into a single column to save memory in a way that is easy to use? For example, storing using a comma-delimited field is not easy and uses memory.
Any ideas?
If a user can have multiple roles, it is probably better to have a user_role table that stores this information. It is normalised, and will be much easier to query.
A table like:
user_id | role
--------+-----------------
1 | Admin
2 | User
2 | Admin
3 | User
3 | Author
Will allow you to query for all users with a particular role, such as SELECT user_id, user.name FROM user_role JOIN user WHERE role='Admin' rather than having to use string parsing to get details out of a column.
Amongst other things this will be faster, as you can index the columns properly and will take marginally more space than any solution that puts multiple values into a single column - which is antithetical to what relational databases are designed for.
The reason this shouldn't be stored is that it is inefficient, for the reason DCoder states on the comment to this answer. To check if a user has a role, every row of the user table will need to be scanned, and then the "roles" column will have to be scanned using string matching - regardless of how this action is exposed, the RMDBS will need to perform string operations to parse the content. These are very expensive operations, and not at all good database design.
If you need to have a single column, I would strongly suggest that you no longer have a technical problem, but a people management one. Adding additional tables to an existing database that is under development, should not be difficult. If this isn't something you are authorised to do, explain to why the extra table is needed to the right person - because munging multiple values into a single column is a bad, bad idea.
You can also use bitwise logic with MySQL. role_id must be in BASE 2 (0, 1, 2, 4, 8, 16, 32...)
role_id | label
--------+-----------------
1 | Admin
2 | User
4 | Author
user_id | name | role
--------+-----------------
1 | John | 1
2 | Steve | 3
3 | Jack | 6
Bitwise logic allows you to select all user roles
SELECT * FROM users WHERE role & 1
-- returns all Admin users
SELECT * FROM users WHERE role & 5
-- returns all users who are admin or Author because 5 = 1 + 4
SELECT * FROM users WHERE role & 6
-- returns all users who are User or Author because 6 = 2 + 4
From your question what I got,
Suppose, you have to table. one is "meal" table and another one is "combo_meal" table. Now I think you want to store multiple meal_id inside one combo_meal_id without separating coma[,]. And you said that it'll make your DB to more standard.
If I not getting wrong from your question then please read carefully my suggestion bellow. It may be help you.
First think is your concept is right. Definitely it'll give you more standard DB.
For this you have to create one more table [ example table: combo_meal_relation ] for referencing those two table data. May be one visible example will clear it.
meal table
+------+--------+-----------+---------+
| id | name | serving | price |
+------+--------+-----------+---------+
| 1 | soup1 | 2 person | 12.50 |
+------+--------+-----------+---------+
| 2 | soup2 | 2 person | 15.50 |
+------+--------+-----------+---------+
| 3 | soup3 | 2 person | 23.00 |
+------+--------+-----------+---------+
| 4 | drink1 | 2 person | 4.50 |
+------+--------+-----------+---------+
| 5 | drink2 | 2 person | 3.50 |
+------+--------+-----------+---------+
| 6 | drink3 | 2 person | 5.50 |
+------+--------+-----------+---------+
| 7 | frui1 | 2 person | 3.00 |
+------+--------+-----------+---------+
| 8 | fruit2 | 2 person | 3.50 |
+------+--------+-----------+---------+
| 9 | fruit3 | 2 person | 4.50 |
+------+--------+-----------+---------+
combo_meal table
+------+--------------+-----------+
| id | combo_name | serving |
+------+--------------+-----------+
| 1 | combo1 | 2 person |
+------+--------------+-----------+
| 2 | combo2 | 2 person |
+------+--------------+-----------+
| 4 | combo3 | 2 person |
+------+--------------+-----------+
combo_meal_relation
+------+--------------+-----------+
| id | combo_meal_id| meal_id |
+------+--------------+-----------+
| 1 | 1 | 1 |
+------+--------------+-----------+
| 2 | 1 | 2 |
+------+--------------+-----------+
| 3 | 1 | 3 |
+------+--------------+-----------+
| 4 | 2 | 4 |
+------+--------------+-----------+
| 5 | 2 | 2 |
+------+--------------+-----------+
| 6 | 2 | 7 |
+------+--------------+-----------+
When you search inside table then it'll generate faster result.
search query:
SELECT m.*
FROM combo_meal cm
JOIN meal m
ON m.id = cm.meal_id
WHERE cm.combo_id = 1
Hopefully you understand :)
You could do something like this
INSERT INTO table (id, roles) VALUES ('', '2,3,4');
Then to find it use FIND_IN_SET
As you might already know, storing multiple values in a cell goes against 1NF form. If youre fine with that, using a json column type is a great way and has good methods to query properly.
SELECT * FROM table_name
WHERE JSON_CONTAINS(column_name, '"value 2"', '$')
Will return any entry with json data like
[
"value",
"value 2",
"value 3"
]
Youre using json, so remember, youre query performance will go down the drain.

How to update a 'Sort Index' column of a list of records with one call

Basically, I have a list of records in a mysql db. These records are ordered 1 to 10. The user can re-order these records to whatever order they want. They will press a button to update all the records to their newly, respective order number. For example:
ID | Sort_Index | Name
----------------------
1 | 1 | Jim
2 | 2 | Bob
3 | 3 | Carl
4 | 4 | Bill
5 | 5 | Wendy
The user can change these to this for example:
Note: the changed values are stored into an array before I make the UPDATE calls
ID | Sort_Index | Name
----------------------
1 | 1 | Carl
2 | 2 | Wendy
3 | 3 | Bob
4 | 4 | Jim
5 | 5 | Bill
My question is, how can I make this mysql call with one call, using the new values in my array, instead of one call for each record?
If this is impossible or simply the "wrong way to do it", please feel free to suggest new ideas as I am not fully committed to this idea as of now.
If you have a limited number of rows, you could implement this with an sql CASE statement --
Update users set sort_index = case id when 1 then <newval> when 2 then <newval>...