EAV how to store additional field value? - mysql

I have a EAV structure in DB and tables: attribute(id,parent_id,code,name) and value(id,entity_id,attribute_id,value).
I'm using parent_id to store attribute's list. For example,
id | parent_id | code | name
1 | null | color_id | Color
2 | 1 | null | Red
3 | 1 | null | Blue
4 | 1 | null | Other
Now I need the ability to store different data on select certain item. This may be one or more inputs. For example:
Color:
(o) Red
(o) Blue
(o) Other (please specify) ______________
How to store it? Create additional table or may be store with parent_id pointed to selected element or something else?
id | parent_id | code | name
4 | 1 | null | Other
5 | 4 | other_one | First text value
6 | 4 | other_two | Second text value

Thought a little bit about it... Here's a try to give you an answer to the question "How to store it":
Make sure, that you get partial structure into your concept. Define kind of types, value packages, things which belong together and use XML. Something like the above could loke like:
<settings>
<parent id="1">
<colors>
<color>Red</color> --here you should think of tags to set the meaning of the color...
<color>Red</color>
<color>Other</color> --here you set whatever color you want
</colors>
</parent>
<parent id="2">
-- Your example with the "First text value" gives me this idea:
<colors>
<color meaning="BackColor">Red</color>
<color meaning="BorderColor">Red</color>
<color meaning="First text value">Blue</color>
<color meaning="Second text value">Green</color>
</colors>
</parent>
</settings>
So my suggestion:
Try to define certain types like "FormColor" and let it be like
<FormColor backcolor="Red" forecolor="blue" first_text="green">
--You may add specialities within the FormColor-element
<SpecialColor meaning="SomeSpecialMeaning">Blue</SpecialColor>
</FormColor>
Place these "types" within a parent element. You can easily pick the parent element via id and read its inner XML. There you find everything you need...
With this approach you are as flexible as with your EAV thing, but much better readable, maintainable and better to evaluate.

Related

DB structure - form with dynamic number of options

I've been reading similar questions, but I think my case is a bit more complicated.
I have a form that register items. These items may have options with sub-options (checkboxes and radio buttons):
The number of checkboxes and radio buttons may decrease/increase but the real pain to design a good structure is for the checkboxes, as these must have (at least I think so) a fixed name column for each one.
The case for radio buttons is easier as I just assign an id to each one (and save the names in a different table).
My current DB structure is simple (between parenthesis is the table/column name):
The items table (item) have columns of type integer (to save the id of the radio buttons).
Another table for the checkboxes (item_option), with columns of type integer (1 if checked, 0 if unchecked). And 1 PK column (item_id) that points to the PK column (id) of the items table.
And tables (again item_option) for the names of the radio buttons with a PK column (id) that points to the option column (is this understandable? Sorry for my bad english).
I think a different table containing the sub-options is better than put all the columns in the main table, right?
So, the radio buttons are stored in the main table (1 column per option) and the checkboxes in a separeted table (1 table per option):
Items table:
+-----+----------+----------+
| id | Option_1 | Option_2 |
+-----+----------+----------+
| 123 | 3 | 1 |
+-----+----------+----------+
| 456 | 2 | 3 |
+-----+----------+----------+
| 789 | 1 | 2 |
+-----+----------+----------+
item_option_3 table (this would be needed to know which ones are checked):
+--------------+--------------+--------------+---------+
| Sub_Option_1 | Sub_Option_2 | Sub_Option_3 | item_id |
+--------------+--------------+--------------+---------+
| 1 | 0 | 1 | 123 |
+--------------+--------------+--------------+---------+
| 1 | 1 | 0 | 456 |
+--------------+--------------+--------------+---------+
| 0 | 1 | 1 | 789 |
+--------------+--------------+--------------+---------+
item_option_1-2 table (this would be used to print the names):
+-----------+--------------+--------------+
| option_id | name | name_es |
+-----------+--------------+--------------+
| 1 | Sub_Option_1 | Sub_Opción_1 |
+-----------+--------------+--------------+
| 2 | Sub_Option_2 | Sub_Opción_2 |
+-----------+--------------+--------------+
| 3 | Sub_Option_3 | Sub_Opción_3 |
+-----------+--------------+--------------+
What kind of structure do I need to spawn these sub options (checkboxes) dynamically?
What about something like this?
Your model has option keys as columns and values as rows. Why have both keys and values be rows? If you don't need complex type-based validation, it should suffice to have a single options table with a one to optionally many relationship to itself to account for suboptions. To enumerate all options and values, just retrieve all rows from the table. If ParentOptionId is null, then it is a base-level option; otherwise it is a suboption.
UML & ER version below.
EDIT: After reading through your question and comments again, I've come up with a more complicated but more robust design for you to consider:
It works like this:
Every user input is an Option. Every option consists of a display text (OptionText), tooltip/subtext/etc (Description), a default and then user supplied value (Value), a value type (ValueType boolean,text, date, etc). It also has a DisplayOrder so you know where to situate it in relation to other Options in its group. Options can also have a parent/child relationship with other Options. You can do the same for the other entities if you want but I did not model that.
Every Option is contained within an OptionGroup with 0 or more sibling Options. OptionGroups are just a collection of one or more related Options. The GroupType field dictates how your form builder needs to treat that group. The most obvious example would be for your radio button groups; each of those would be an OptionGroup and each radio button would be a boolean Option within the OptionGroup. An OptionGroup could just as easily handle a multiple selection checkbox group or just some related text inputs that need a common header text (like a street address).
For further dynamic design OptionGroups are contained within GroupSections, even if there is just one default GroupSection in a form.
Finally, a Form models your final actual UI form and consists of one or more GroupSections.
This should be flexible enough for you to tweak to your liking. What do you think?
Final note: if you are looking into dynamically building your forms in Javascript, check out a few frameworks like X-editable or formly. They take JSON or configuration objects and build out the entire form with validation/etc from there while giving you some hooks for event handling. Chances are you don't need to completely reinvent the wheel unless you want to keep your implementation as simple and specific as possible.

Related Key/Value table indexing / search

I have table 'elements' that is related with table 'element_parameters'. element_parameters table have key/value structure. Keys and values are dynamicaly, they can be created be user or by some software, so I can't predefine them in 'elements' table.
For example I have elements 'flower' and 'car':
elements
--------
flower
car
element_parameters
------------------
element | key | value
flower | smell | soft
car | manufacturer | fiat
flower | color | red
car | wheels | 4
As you can see, there could be any keys and any values.
But often I need to find elements by their parameters. This is the problem. One element could have 10000+ parameters. So if I have 100 elements, I have to search through 1000000 parameters each time i want to find some element. And i can't do this in one query, be cause first I have to find all parameters thats fits my condition, group them by parameter ID and then search elements table for those ID's.
Is there some search engines, where I could merge and index elements with their parameters to 'one row'? Elasticsearch would be good decision for this situation? Maybe there is other ways to solve it?
Thank you.
I think you should modify your db like this:
elements
-----------
id | name
1 | flower
2 | car
element_parameters
-------------------------------------------------
element_id | parameters
1 | {"color":"red","smell":"soft"}
2 | {"manufacturer": "fiat", "wheels":3}
You can find easily any element's parameter:
$id = 1;
$element = Element::find($id)->load('ElementParameters')->get();
$paremeters = json_decode($element->ElementParameters);

How to compare values of text columns from the same table

I have a table in which each column represent a text from a manuscript, here is a simple example:
mss1 | mss2 | mss3
------------------------
The | The | A
big | big | big
black | |
dog | dog | dog
I would like to display rows where two columns have different values (or the same values), for instance I want to see the differences between mss1 and mss3. The result should look like:
mss1 | mss3
---------------
The | A
black |
These seemed to be good solution candidates :
SELECT mss1, mss3 FROM table WHERE mss1 != mss3;
SELECT mss1, mss3 FROM table WHERE mss1 NOT LIKE mss3;
However it is not working, even after converting all columns from text to varchar of the same length.
I also tried LOCATE (See here) to find same values: if I can locate mss1 in mss3 and vice-versa, they must have the same value, right? But that was not successful either.
Any idea? It seems like it should be easy, but I can't figure it out...
Perhaps the issue with your query is that NULL values are filtered out. For your purposes, I think this may do what you want:
SELECT mss1, mss3
FROM table
WHERE coalesce(mss1, '') <> coalesce(mss3, '');

Output data from a MySQL table one time with php

I have a mysql table with a column that looks something like this:
| TAGS |
------
|Green |
|Blue |
|Orange|
|Blue |
|Green |
| ... |
------
Now what I want to do is output all the different tags that exist in a list, BUT every tag can only be outputted once (so e.g. 'Green stands two times in the database but can only stands one time in the list')
Hope you understand my question!
Thanks
You said "row" but do you mean a column labeled "tags" in your table?
There are two different ways:
Method 1:
SELECT DISTINCT tags FROM table_name WHERE condition;
Method 2:
SELECT tags FROM table_name WHERE condition GROUP BY tags;
They both will return a you an array where each item in the row is the tag without repeats (distinct). The main difference is that DISTINCT makes it easier to optimize (and possibly quicker).
Now, if you didn't make a typo and said that you have a row with a column that has multiple tags like:
|ROW_ID|TAGS |
| 1 |'Blue', 'Red' |
| 2 |'Red', 'Yellow' |
| 3 |'Blue', 'Black', 'Red'|
Then you'll have to do some parsing and array operations (but that's a completely different answer).

How do I resolve or avoid need for MySQL with multiple AUTO INCREMENT columns?

I have put a lot of effort into my database design, but I think I am
now realizing I made a major mistake.
Background: (Skip to 'Problem' if you don't need background.)
The DB supports a custom CMS layer for a website template. Users of the
template are limited to turning pages on and off, but not creating
their own 'new' pages. Further, many elements are non editable.
Therefore, if a page has a piece of text I want them to be able to edit,
I would have 'manually' assigned a static ID to it:
<h2><%= CMS.getDataItemByID(123456) %></h2>
Note: The scripting language is not relevant to this question, but the design forces
each table to have unique column names. Hence the convention of 'TableNameSingular_id'
for the primary key etc.
The scripting language would do a lookup on these tables to find the string.
mysql> SELECT * FROM CMSData WHERE CMSData_data_id = 123456;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| 1 | 123456 | 1 |
+------------+-----------------+-----------------------------+
mysql> SELECT * FROM CMSDataTypes WHERE CMSDataType_type_id = 1;
+----------------+---------------------+-----------------------+------------------------+
| CMSDataType_id | CMSDataType_type_id | CMSDataType_type_name | CMSDataType_table_name |
+----------------+---------------------+-----------------------+------------------------+
| 1 | 1 | String | CMSStrings |
+----------------+---------------------+-----------------------+------------------------+
mysql> SELECT * FROM CMSStrings WHERE CMSString_CMSData_data_id=123456;
+--------------+---------------------------+----------------------------------+
| CMSString_id | CMSString_CMSData_data_id | CMSString_string |
+--------------+--------------------------------------------------------------+
| 1 | 123456 | The answer to the universe is 42.|
+--------------+---------------------------+----------------------------------+
The rendered text would then be:
<h2>The answer to the universe is 42.</h2>
This works great for 'static' elements, such as the example above. I used the exact same
method for other data types such as file specifications, EMail Addresses, Dates, etc.
However, it fails for when I want to allow the User to dynamically generate content.
For example, there is an 'Events' page and they will be dynamically created by the
User by clicking 'Add Event' or 'Delete Event'.
An Event table will use keys to reference other tables with the following data items:
Data Item: Table:
--------------------------------------------------
Date CMSDates
Title CMSStrings (As show above)
Description CMSTexts (MySQL TEXT data type.)
--------------------------------------------------
Problem:
That means, each time an Event is created, I need to create the
following rows in the CMSData table;
+------------+-----------------+-----------------------------+
| CMSData_id | CMSData_data_id | CMSData_CMSDataType_type_id |
+------------+-----------------+-----------------------------+
| x | y | 6 | (Event)
| x+1 | y+1 | 5 | (Date)
| x+2 | y+2 | 1 | (Title)
| x+3 | y+3 | 3 | (Description)
+------------+-----------------+-----------------------------+
But, there is the problem. In MySQL, you can have only 1 AUTO INCREMENT field.
If I query for the highest value of CMSData_data_id and just add 1 to it, there
is a chance there is a race condition, and someone else grabs it first.
How is this issue typically resolved - or avoided in the first place?
Thanks,
Eric
The id should be meaningless, except to be unique. Your design should work no matter if the block of 4 ids is contiguous or not.
Redesign your implementation to add the parts separately, not as a block of 4. Doing so should simplify things overall, and improve your scalability.
What about locking the table before writing into it? This way, when you are inserting a row in the CMSData table, you can get the last id.
Other suggestion would be to not have an incremented id, but a unique generated one, like a guid or so.
Lock Tables