MySQL query GROUP BY WHERE IN - mysql

I have a table like:
+------+--------+-----------+
| | name | type_id |
+------+--------+-----------+
| 1 | Bob | 3 |
| 2 | Tony | 2 |
| 3 | Sheila | 2 |
| 4 | Sarah | 8 |
| 5 | Tom | 7 |
+------+--------+-----------+
and I want to group my type_id into a new column, called 'type' . type_id 2 and 8 would have the value 'yes', everything else 'no' so my results would look something like:
+------+--------+-----------+------+
| | name | type_id | type |
+------+--------+-----------+------+
| 1 | Bob | 3 | no |
| 2 | Tony | 2 | yes |
| 3 | Sheila | 2 | yes |
| 4 | Sarah | 8 | yes |
| 5 | Tom | 7 | no |
+------+--------+-----------+------+
Is this even possible, if so whats it called, as I've searched the docs on 'GROUP BY' and 'JOINS' , but couldn't see a solution like this.

You can do that with a case:
SELECT *,CASE WHEN type_id IN (2,8) THEN 'yes' ELSE 'no' END as `type`
FROM yourTable
sqlfiddle demo

Related

MySql - How to sort vertically stored data to order by one of field's values horizontally?

I have 3 mysql tables named records, fields and fields_values.
Records table consists of meta data about records and record id.
Fields table
No. of fields for a record are variable, and can be added dynamically.
Fields values contain info about fields shown on the record form eg: what type of fields is it, whether it is required or no etc.
Fields values table
This table contains actual data of records for each field.
For example, these tables have data as below:
Records Table:
+----+-------------+------------+--------+
| id | category_id | created_by | status |
+----+-------------+------------+--------+
| 1 | 1 | 10 | 1 |
| 2 | 1 | 10 | 1 |
| 3 | 1 | 10 | 1 |
+----+-------------+------------+--------+
Fields Table:
+----+------------+------------+------+--------+----------+------------+
| id | title | alias | type | status | required | created_by |
+----+------------+------------+------+--------+----------+------------+
| 1 | First Name | first_name | text | 1 | 1 | 100 |
| 2 | Last Name | last_name | text | 1 | 1 | 100 |
| 3 | City | city | text | 1 | 1 | 100 |
| 4 | State | state | text | 1 | 1 | 100 |
| 5 | Country | country | text | 1 | 1 | 100 |
| 6 | Mobile | mobile | text | 1 | 1 | 100 |
+----+------------+------------+------+--------+----------+------------+
Fields Values Table:
+----+----------+-----------+------------+
| id | field_id | record_id | value |
+----+----------+-----------+------------+
| 1 | 1 | 1 | Andy |
| 2 | 2 | 1 | A |
| 3 | 3 | 1 | Manchester |
| 4 | 4 | 1 | NWE |
| 5 | 5 | 1 | UK |
| 6 | 6 | 1 | 1234567898 |
| 7 | 1 | 2 | Sandy |
| 8 | 2 | 2 | B |
| 9 | 3 | 2 | NYC |
| 10 | 4 | 2 | NY |
| 11 | 5 | 2 | USA |
| 12 | 6 | 2 | 1234567891 |
| 13 | 1 | 3 | Mandy |
| 14 | 2 | 3 | P |
| 15 | 3 | 3 | Mumbai |
| 16 | 4 | 3 | MH |
| 17 | 5 | 3 | IN |
| 18 | 6 | 3 | 1234567893 |
+----+----------+-----------+------------+
And, I want to records as below and want to sort it based on one of the fields as selected by user eg: country
+----+------------+-----------+------------+-------+-----------+------------+
| id | first_name | last_name | city | state | country ^ | mobile |
+----+------------+-----------+------------+-------+---------+------------+
| 3 | Mandy | P | Mumbai | MH | IN | 1234567893 |
| 1 | Andy | A | Manchester | NWE | UK | 1234567898 |
| 2 | Sandy | B | NYC | NY | USA | 1234567891 |
+----+------------+-----------+------------+-------+-----------+------------+
How do I sort such vertically stored data to order by one of field's values in a single query so that it can be shown horizontally?
Just to observe that it would be more usual to write that this way...
SELECT r.id
, MAX(CASE WHEN field_id = 1 THEN value END) first_name
, MAX(CASE WHEN field_id = 2 THEN value END) last_name
, MAX(CASE WHEN field_id = 3 THEN value END) city
, MAX(CASE WHEN field_id = 4 THEN value END) state
, MAX(CASE WHEN field_id = 5 THEN value END) country
, MAX(CASE WHEN field_id = 6 THEN value END) mobile
FROM records r
LEFT
JOIN fields_values v
ON v.record_id = r.id
GROUP
BY r.id;

Row to column transform

After multiple join, I have raw results.
+----------------------------------------------------------------------+
| Results |
+----+----------+-------------+----------+-----------+-----------------+
| id | group_id | question_id | question | answer_id | answer | input |
+----+----------+-------------+----------+-----------+-----------------+
| 1 | 10001 | 1 | How old | 1 | 25 | NULL |
| 2 | 10001 | 2 | What like| 3 | Cola | NULL |
| 3 | 10001 | 2 | What like| 4 | Other | HotDog |
| 4 | 10001 | 3 | City | 5 | NYC | NULL |
| 5 | 10001 | 4 | Name | 7 | Other | Alex |
| 6 | 10002 | 1 | How old | 1 | 25 | NULL |
| 7 | 10002 | 2 | What like| 6 | Candy | NULL |
| 8 | 10002 | 3 | City | 8 | LA | NULL |
| 9 | 10002 | 4 | Name | 7 | Other | Roman |
+----+----------+-------------+----------+-----------+--------+--------+
But now I want to see it in "one row view" by group_id.
Such as:
+----+----------+-------------+----------+-----------+
| id | How Old | What like | City | Name |
+----+----------+-------------+----------+-----------+
| 1 | 25 | Cola,HotDog | NYC | Alex |
| 2 | 25 | Candy | LA | Roman |
+----+----------+-------------+----------+-----------+
I don`t know normal group_by/concat construction for that. What must I do?
SET #i := 1;
SELECT #i := #i + 1 AS `id`
, GROUP_CONCAT(CASE WHEN question = 'How old' THEN answer ELSE NULL END) AS `How Old`
, GROUP_CONCAT(CASE WHEN question = 'What like' THEN IF(answer='Other', input, answer) ELSE NULL END) AS `What like`
, ....
FROM theTable
GROUP BY group_id
;
group_concat ignores null values, only returning null if the only values it received were null. For the CASE WHEN THEN ELSE END statements you could easily use IF(,,) like was used in the "other" check; but CASE is more portable (MS SQL Server only relatively recently added support for those kinds of IFs.)

Counting and sorting multiple columns

I have a table in MySQL like this:
+---------+---------+---------+---------+
| teacher | grade_1 | grade_2 | grade_3 |
+---------+---------+---------+---------+
| Paul | 1 | 0 | 3 |
| Mike | 2 | 2 | 1 |
| Jens | 3 | NULL | 2 |
| Jens | 2 | 3 | NULL |
| Mike | 1 | 2 | 1 |
| Paul | 2 | 2 | 2 |
| Mike | 1 | NULL | NULL |
+---------+---------+---------+---------+
I would like to produce an outcome like this:
+---------+-------+-------+
| teacher | grade | count |
+---------+-------+-------+
| Jens | NULL | 2 |
| Jens | 0 | 0 |
| Jens | 1 | 0 |
| Jens | 2 | 2 |
| Jens | 3 | 2 |
| Mike | NULL | 2 |
| Mike | 0 | 0 |
| Mike | 1 | 4 |
| Mike | 2 | 3 |
| Mike | 3 | 0 |
| Paul | NULL | 0 |
| Paul | 0 | 1 |
| Paul | 1 | 1 |
| Paul | 2 | 3 |
| Paul | 3 | 1 |
+---------+-------+-------+
So, every grade could have four different distinct values; NULL, 0, 1, 2 or 3
For every teacher, I would like to count the occurences of each grade in every grade column in total.
Is this possible? The actual table is much bigger with many more grade columns and more teachers.
My brain's totally stuck and I can't seem to think of a way to do it...
Best regards,
Jens

Mysql query to convert table from long format to wide format

I have a table called ContactAttrbiutes which contains a list of each contacts' attributes. The kind of data stored for these contacts include: Title, Forename, Surname telephone number etc.
Current Table
+-------------+-----------+------------------------------+
| attributeId | ContactId | AttributeValue |
+-------------+-----------+------------------------------+
| 1 | 5 | Lady |
| 2 | 5 | Elizabeth |
| 3 | 5 | E |
| 4 | 5 | Anson |
| 5 | 5 | |
| 6 | 5 | |
| 7 | 5 | |
| 8 | 5 | |
| 10 | 5 | 0207 72776 |
| 11 | 5 | |
| 12 | 5 | 0207 22996 |
| 13 | 5 | 0207 72761 |
| 14 | 5 | |
| 15 | 5 | |
| 60 | 5 | Lloyds |
| 61 | 5 | |
| 1 | 10 | Mr |
| 2 | 10 | John |
| 3 | 10 | J C |
| 4 | 10 | Beveridge |
| 5 | 10 | Esq QC |
| 6 | 10 | Retired |
| 7 | 10 | |
| 8 | 10 | |
| 10 | 10 | 0207 930 |
| 11 | 10 | |
| 12 | 10 | |
| 13 | 10 | 0207 930 |
| 14 | 10 | |
| 15 | 10 | |
| 60 | 10 | |
| 61 | 10 | |
+-------------+-----------+------------------------------+
However I would like to run a query to create a table that looks like...
New Table
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| ContactId | AttributeValue_Title | AttributeValue_ForeName |AttributeValue_Initial | AttributeValue_Surname |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 5 | Lady | Elizabeth | E | Anson |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 10 | Mr | John | J C | Beveridge |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
I am sure there is a very simple answer but I have spent hours looking. Can anyone help?
The above is only a small extract of my table, I have 750,000 contacts. In addition I would like the final table to have more columns than I have described above but they will come from different Attributes with the existing table.
Thank you very much in advance.
try this
SELECT ContactId ,
max(CASE when attributeId = 1 then AttributeValue end) as AttributeValue_Title ,
max(CASE when attributeId = 2 then AttributeValue end )as AttributeValue_ForeName ,
max(CASE when attributeId = 3 then AttributeValue end )as AttributeValue_Initial ,
max(CASE when attributeId = 4 then AttributeValue end) as AttributeValue_Surname
from Table1
group by ContactId
DEMO HERE
if you want to make your result more longer for other attributeId then just add a case statment as in the code.
SELECT
t_title.AttributeValue AS title,
t_name.AttributeValue AS name,
...
FROM the_table AS t_title
JOIN the_table AS t_firstname USING(contact_id)
JOIN ...
WHERE
t_title.attributeId = 1 AND
t_firstname.attributeId = 2 AND
...
EAV "model" is an antipattern in most cases. Are you really going to have a variable number of attributes? If yes, then no-SQL solution might be more appropriate than a relational database.

Tricky database design

I need to design a database to store user values : for each user, there is a specific set of columns.
For instance, Jon wants to store values in a table with 2 columns : name, age.
And Paul wants to store values in a 3 columns table : fruit, color, weight.
At this point, I have 2 options.
Option 1 - Store data as text values
I would have a first table 'profiles' with the users' preferences :
+----+---------+--------+-------------+
| id | user_id | label | type |
+----+---------+--------+-------------+
| 1 | 1 | name | VARCHAR(50) |
| 2 | 1 | age | INT |
| 3 | 2 | fruit | VARCHAR(50) |
| 4 | 2 | color | VARCHAR(50) |
| 5 | 2 | weight | DOUBLE |
+----+---------+--------+-------------+
And then store the datas as text in another table :
+----+------------+--------+
| id | id_profile | value |
+----+------------+--------+
| 1 | 1 | Aron |
| 2 | 2 | 17 |
| 3 | 1 | Vince |
| 4 | 2 | 27 |
| 5 | 1 | Elena |
| 6 | 2 | 78 |
| 7 | 3 | Banana |
| 8 | 4 | Yellow |
| 9 | 5 | 124.8 |
+----+------------+--------+
After that, I would programatically create and populate a clean table.
Option 2 - One column per type
On this option, I would have a first table 'profiles2' like that :
+----+---------+--------+------+
| id | user_id | label | type |
+----+---------+--------+------+
| 1 | 1 | name | 3 |
| 2 | 1 | age | 1 |
| 3 | 2 | fruit | 3 |
| 4 | 2 | color | 3 |
| 5 | 2 | weight | 2 |
+----+---------+--------+------+
with the type corresponding of a set of type : 1=INT , 2=DOUBLE , 3=VARCHAR(50)
And a data table like that :
+----+-------------+-----------+--------------+---------------+
| id | id_profile2 | int_value | double_value | varchar_value |
+----+-------------+-----------+--------------+---------------+
| 1 | 1 | NULL | NULL | Aron |
| 2 | 2 | 17 | NULL | NULL |
| 3 | 1 | NULL | NULL | Vince |
| 4 | 2 | 27 | NULL | NULL |
| 5 | 1 | NULL | NULL | Elena |
| 6 | 2 | 78 | NULL | NULL |
| 7 | 3 | NULL | NULL | Banana |
| 8 | 4 | NULL | NULL | Yellow |
| 9 | 5 | NULL | 124.8 | NULL |
+----+-------------+-----------+--------------+---------------+
Here I have cleaner tables, but still a programmatic trick to implement to get everything in order.
The questions
Have anyone ever face this situation ?
What do you think of my 2 options ?
Is there a better solution, less tricky ?
Tx a lot!
Edit
Hi again,
My model had a bug : impossible to retrieve a "line" of information; i.e. the informations in the "values" table are not sortables.
After some wanredings around the EAV model, it showed not suitable because it's not designed to store datas, but specific infos.
Then I ended with this model :
Firt table 'labels' :
+----+------------+------+----------+
| id | profile_id | name | datatype |
+----+------------+------+----------+
| 1 | 1 | 1 | Nom |
| 2 | 1 | 1 | Age |
| 3 | 2 | 2 | Fruit |
| 4 | 2 | 2 | Couleur |
| 5 | 2 | 2 | Poids |
+----+------------+------+----------+
Then a very simple 'nodes' talbe, just to keep track of the lines of infos :
+----+------------+
| id | profile_id |
+----+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
+----+------------+
and a set of tables corresponding to different datatypes :
+----+---------+----------+--------+
| id | node_id | label_id | value |
+----+---------+----------+--------+
| 1 | 1 | 1 | John |
| 2 | 2 | 1 | Doe |
| 3 | 3 | 3 | Orange |
| 4 | 3 | 4 | Orange |
| 5 | 4 | 3 | Banane |
| 6 | 4 | 4 | Jaune |
+----+---------+----------+--------+
With this model, queries are ok. Data input is a bit tricky but I will manage with a clean code.
Cheers
Take a look at EAV data models.
Option 3: make two different tables.
One table is obviously for people. The other is obviously for fruit. They should be in different tables.
Why not just have a user table with name and ID, the a userValues table that has key value pairs? that was John can have key "fruit" and value "mango, and another key "tires" and value "goodyear". Bob can have key "coin" and value "penny" and key "age" and value "42". Anyone can have any value they like and you have maximum flexibility. Speed won't be great, and you'll have to cast string to values, but it's always a tradeoff.
Cheers,
Daniel