MySQL: how to convert to EAV - Part 3? - mysql

Previous Related Posts:
MySQL: how to convert to EAV?
MySQL: how to convert to EAV - Part 2?
Given a table:
TABLE: foo
===============================
| id | first_name | last_name |
===============================
| 1 | John | Doe |
| 2 | Jane | Smith |
| 3 | Ronald | McDonald |
-------------------------------
How do I take this table and convert it to these tables (an EAV implementation)?:
TABLE: attribute
===========================
| id | fk_id | attribute |
===========================
| 1 | 100 | first_name |
| 2 | 100 | last_name |
---------------------------
TABLE: value
=========================================
| id | attribute_id | row_id | value |
=========================================
| 1 | 1 | 1 | John |
| 2 | 2 | 1 | Doe |
| 3 | 1 | 2 | Jane |
| 4 | 2 | 2 | Smith |
| 5 | 1 | 3 | Ronald |
| 6 | 2 | 3 | McDonald |
-----------------------------------------
NOTES:
attribute.fk_id will be provided.
value.row_id is used to identify how the values are grouped as records in the original table.
UPDATE: Also, how do I query the EAV tables so that I can make it look like table foo again.

I give +1 to #Phil's solution for populating the EAV table. Insert one attribute at a time.
Here's another solution to reverse an EAV transformation:
SELECT v.row_id AS id,
MAX(IF(a.attribute='first_name',v.value,NULL)) AS first_name,
MAX(IF(a.attribute='last_name',v.value,NULL)) AS last_name
FROM value v INNER JOIN attribute a
ON v.attribute_id = a.id
GROUP BY v.row_id
Except that by using EAV, you've put all your values into a column of VARCHAR(255) or whatever, so you have lost information about the respective data types of the original columns.
There's really no way to do it "dynamically" without hard-coding the attribute names, either as joins as #Phil shows, or as columns as I show. It's essentially the same problem as trying to write dynamic pivot queries.
I have written more about EAV in my presentation Practical Object-Oriented Models in SQL and in my book, SQL Antipatterns Volume 1: Avoiding the Pitfalls of Database Programming.

I think your only hope is if you use the foo table. bar is essentially useless without the ID.
Try something like this (assuming attribute.id is an auto-increment primary key)
INSERT INTO `attribute` (`fk_id`, `attribute`)
VALUES (100, 'first_name');
INSERT INTO `value` (`attribute_id`, `row_id`, `value`)
SELECT LAST_INSERT_ID(), `id`, `first_name`
FROM `foo`;
INSERT INTO `attribute` (`fk_id`, `attribute`)
VALUES (100, 'last_name');
INSERT INTO `value` (`attribute_id`, `row_id`, `value`)
SELECT LAST_INSERT_ID(), `id`, `last_name`
FROM `foo`;
To reconstruct the foo table, try this
SELECT
`fn`.`row_id` AS `id`,
`fn`.`value` AS `first_name`,
`ln`.`value` AS `last_name`
FROM `value` `fn`
INNER JOIN `attribute` `fn_a`
ON `fn`.`attribute_id` = `fn_a`.`id`
AND `fn_a`.`attribute` = 'first_name'
INNER JOIN `value` `ln`
ON `fn`.`row_id` = `ln`.`row_id`
INNER JOIN `attribute` `ln_a`
ON `ln`.`attribute_id` = `ln_a`.`id`
AND `ln_a`.`attribute` = 'last_name'
Ergh, thanks for reminding me why I hate this pattern

Related

MySql Error - Delete Duplicates Rows with priority

Pre-Information
I've table called Test:
-- Table Creation
CREATE TABLE Test(
id integer,
title varchar (100)
);
-- Insertion
INSERT INTO Test Values
(1, "Hi"),
(2, 'Hello'),
(2, "Hellew"),
(3, "World"),
(3,"Wordy");
Test Table
| Id | title |
|----|--------|
| 1 | Hi |
| 2 | Hello |
| 2 | Hellew |
| 3 | World |
| 3 | Wordy |
Process
I want to delete the duplicates id
Based on the priority
The Problem
This is the output Error I get
ERROR 1093 (HY000) at line 5: You can't specify target table 'Test' for update in FROM clause
Required OUTPUT
| Id | title |
|----|--------|
| 1 | Hi |
| 2 | Hello |
| 3 | World |
Thanks,
You have not clearly mentioned what is the "priority" here. But looking at the output example, I am assuming that the priority is to keep those strings which are greater than the others with similar id. Here is my code:
delete a.* from Test a join Test b
on a.id = b.id where a.title < b.title;
select * from Test;
Reference
MySQL: ALIASES

Insert data in table using two or more tables

I have two existing table and wants to create third table with help of few columns. The fist two tables are;
Table one: users
|id | name | sid |
| 1 | demo | test1 |
| 2 | anu | test2 |
Table one: insights
| id | description| name |
| 1 | yes | demoone|
| 2 | no | demotwo|
I want to insert data in new table called insight_owner. As per my knowledge, I made below query but that is giving me below error
ERROR 1242 (21000): Subquery returns more than 1 row
The query used is
insert into insight_owner (column_one, column_two, column_three, column_four, column_five) VALUES ('1', '0', NULL, (select u.id from users u where u.sid='test1'), (select i.id from insights i)) ;
Expected output is
| column_one| column_two| column_three| column_four| column_five| column_six |
+----+-----------------+--------------------+---------------+-----------+--------------------+
| 1 | 1 | 1 | NULL | 1 | 1 |
| 2 | 1 | 1 | NULL | 1 | 2 |
column_five = Users id
column_six = Insight id
INSERT...SELECT syntax is what you're looking for (instead of INSERT...VALUES, which is limited to single values per column in each value list). That allows you to select the data directly from the table(s) concerned, using normal SELECT and JOIN syntax. You can also hard-code values which you want to appear on every row, just as you can in a normal SELECT statement. Basically, write the SELECT statement, get it to output what you want. Then stick an INSERT at the start of it and it sends the output to the desired table.
insert into insight_owner (column_one, column_two, column_three, column_four, column_five)
select '1', '0', NULL, (select u.id from users u where u.sid='test1'), i.id
from insights i
You are using
insert into insight_owner (column_one, column_two, column_three, column_four, column_five) VALUES ('1', '0', NULL, (select u.id from users u where u.sid='test1'), (select i.id from insights i));
Which basically inserts one row in your new table.
So, when you add subquery
select i.id from insights i
It will return all rows from insights table an you actually want just one value.
The result you will get is
| id |
| 1 |
| 2 |
And you want
| id |
| 1 |
So, you should be adding conditional that will make sure you are getting only one result as you are doing with first query (where u.sid='test1'), or limit.
I hope this helps.

Insert concatenated values from multiple rows as new row in MySQL query

I currently have a table with columns for "field_type" (username, city, state), "user_id", and "value". The user_id column obviously has lots of repeats. I'd like to merge the city and state data into a single "location" field_type value. I need something that will:
for every integer in the user_id column:
-check if there exist corresponding (not null) table rows for field_type "city" and "state"
-if yes, insert a new row into the table with field_type "location" which concatenates the corresponding city and state values for that user_id
I haven't worked with MySQL much so I don't really know where to start. I've tried to simplify the problem a bit - it's actually a somewhat more complicated wordpress table and I'm trying to reformat the data to be compatible with a new plugin, but this covers the basics of what has to happen so I should hopefully be able to extrapolate an actual solution from the answers. Thanks for any pointers!
Edit: Current structure looks like this:
|-id (key)-|- field_type -|- user_id -|- value -|
| 1 | username | 1 | Joe |
| 2 | city | 1 | Albany |
| 3 | state | 1 | NY |
| 4 | username | 2 | Bob |
| 5 | city | 2 | Toledo |
| 6 | state | 2 | OH |
And I would like to get something like this:
|-id (key)-|- field_type -|- user_id -|- value ---------|
| 1 | username | 1 | Joe |
| 2 | city | 1 | Albany |
| 3 | state | 1 | NY |
| 4 | username | 2 | Bob |
| 5 | city | 2 | Toledo |
| 6 | state | 2 | OH |
| 7 | location | 1 | Albany, NY |
| 8 | location | 2 | Toledo, OH |
Duplicate user_id values are how it's supposed to work, so they don't need to be removed.
You could use an INSERT ... SELECT query to do this:
INSERT INTO yourtable
SELECT 'location' AS field_type, t1.user_id, CONCAT(t1.value, ' ', t2.value) AS value
FROM yourtable t1
JOIN yourtable t2 ON t2.user_id = t1.user_id AND t1.field_type = 'city' AND t2.field_type = 'state'
WHERE t1.value IS NOT NULL AND t2.value IS NOT NULL
Demo on dbfiddle
Since you are mentioned that there are duplicates with the same user_id.
I don't think inserting a new row will be a good idea.
So have written an update query to update the existing data.
You can later cleanup the duplciates.
UPDATE your_table SET locations = CONCAT_WS('-', state, city) where city is not null or state is not null

How to insert data for one column for all the rows in single query in Mysql?

+----+------------+------------+------------+----------+
| id | phone_no | join_date | city | blood_gp |
+----+------------+------------+------------+----------+
| 1 | 80077672xx | 1997-07-19 | Delhi | NULL |
| 2 | 80077642xx | 1998-07-19 | New Delhi | NULL |
| 3 | 80477642xx | 1999-07-19 | Mumbai | NULL |
| 4 | 80077654xx | 1997-05-31 | Kolkata | NULL |
+----+------------+------------+------------+----------+
I want to enter all the blood groups at once . Is there a way to do so ?
you can use single query with select and update
UPDATE table1 , (SELECT * FROM table2 where 1) src
SET table1.blood_gp = src.filed2 where 1 ;
if you want to insert multiple row data using single query then use this code
INSERT INTO yourtable (x,y,z) VALUES (a1,a2,a3), (b1,b2,b3);
or if you want to update one column value all filed then use this code
update yourtable set blood_gp = 'yourvalue' where 1;
if any problem then inform me
Just make an update query without where clause.
update table set blood_gp = 'value'
That's generalize query.

Get row values from foreign key MySQL

This may sound simple and dumb but how can I get the values of a foreign key from a table and displaying it?
I have a table named "subjects" which contains different school subjects and one column of it is a foreign key referencing from table named "faculty".
TABLE subjects
___________________________________
| sub_id | sub_faculty | sub_desc |
| 1 | 2 | PHYSICS |
| 2 | 3 | MATH |
| 3 | 4 | HISTORY |
|________|_____________|__________|
TABLE faculty
________________________
| fac_id | fac_name |
| 2 | John |
| 3 | Mark |
| 4 | Johnny |
|________|_____________|
I firstly wanted to check if data exist in the "subject" table and then proceed on displaying the row values of the foreign key.
I have this not so working query as displays both JOHN and JOHNNY. I was using LIKE as it will be for the search feature of the system so hopefully you can help me out on this.
SELECT *
FROM subject, faculty
WHERE subject.sub_desc = 'PHYSICS'
AND subject.sub_year = '4'
AND faculty.fac_name LIKE '%JOHN%'
GROUP BY faculty.fac_id
SELECT *
FROM subject s
join faculty f
on s.sub_faculty = f.fac_id
WHERE s.sub_desc = 'PHYSICS'
AND s.sub_year = '4'
AND f.fac_name LIKE '%JOHN%'