MySQL incremental columns - mysql

I have a table like below:
ID|Prototype_A|Prototype_B|Prototype_C|Prototype_D|
---------------------------------------------------
1 |Fast381A |Blue4812 | Green7181 | White4812 |
---------------------------------------------------
2 |Slow841C |Orange8312 | null | null |
---------------------------------------------------
3 |Plane281K | null | null | null |
---------------------------------------------------
I need my query to return all non null prototypes for that ID.
so for example:
1 : Fast381A,Blue4812,Green7181,White4812
2 : Slow841C,Orange8312
3 : Plane281K
Is there a way to wildcard select all columns like select(Prototype_*) or should I setup my table in a different format?
For example I've been taught this type of structure is bad practice:
ID|Prototypes|
---------------------------------------------------
1 |Fast381A,Blue4812,Green7181,White4812
---------------------------------------------------
2 |Slow841C,Orange8312
---------------------------------------------------
3 |Plane281K
---------------------------------------------------

A SQL query returns a fixed set of columns. If you want to combine the non-NULL values into a single column, I would recommend concat_ws():
select id,
concat_ws(',', Prototype_A, Prototype_B, Prototype_C, Prototype_D)
from t;
This ignores the NULL values. The query returns two columns, one is a list of prototypes.
And, the answer to your question is "Yes". You should consider changing your data structure. Having multiple columns storing the same thing, with just an index identifying them usually means that you want a separate table, with one row per id and per prototype.
EDIT:
You want a table like this:
create table ModelPrototypes (
ModelProtypeId int primary key auto_increment,
ModelId int not null,
ProtoTypeChar char(1),
Prototype varchar(255)
);
Then you would populate it with values like:
1 A Fast381A
1 B Blue4812
1 C Green7181
1 D White4812
I'm not sure if PrototypeChar is really needed, but the information is in your table.

There's no way to wildcard select columns.
What you could do:
Setup your table as
ID, Prototype_type, Prototype_name
Then use GROUP_CONCAT:
SELECT id, GROUP_CONCAT(Prototype_name SEPARATOR ',')
FROM table GROUP BY Prototype_name

"Should I setup my table in a different format?"
Yes. Your table might look as follows:
ID Prototype_Code Prototype
------------------------------
1 A Fast381A
1 B Blue4812
1 C Green7181
1 D White4812
2 A Slow841C
2 B Orange8312
3 A Plane281K

Related

MySQL - how to get count of a single item frequency in a table of CSV values

I have a mysql table called "projects" with a single field containing CSV lists of project Ids. Assume that I cannot change the table structure.
I need a query that will allow me to quickly retrieve a count of rows that contain a particular project id, for example:
select count(*) from projects where '4' in (project_ids);
This returns just 1 result, which is incorrect (should be 3 results), but I think that it illustrates what I'm attempting to do.
CREATE TABLE `projects` (
`project_ids` varchar(255) DEFAULT NULL
);
INSERT INTO `projects` (`project_ids`)
VALUES
('1,2,4'),
('1,2'),
('4'),
('4,5,2'),
('1,2,5');
I was hoping that there might be a simple mysql function that would achieve this so that I don't have to anything complex sql-wise.
You could use this approach:
SELECT COUNT(*)
FROM projects
WHERE CONCAT(',', project_ids, ',') LIKE '%,4,%';
Or use FIND_IN_SET for a built-in way:
SELECT COUNT(*)
FROM projects
WHERE FIND_IN_SET('4', project_ids) > 0;
But, as to that which Gordon's comment alludes, a much better table design would be to have a junction table which relates a primary key in one table to all projects in another table. That junction table, based off your sample data, would look like this:
PK | project_id
1 | 1
1 | 2
1 | 4
2 | 1
2 | 2
3 | 4
4 | 4
4 | 5
4 | 2
5 | 1
5 | 2
5 | 5
With this design, if you wanted to find the count of PK's having a project_id of 4, you would only need a much simpler (and sargable) query:
SELECT COUNT(*)
FROM junction_table
WHERE project_id = 4;
You would need to use a like condition as follows
select count(*)
from projects
where concat(',',project_ids,',') like '%,4,%';

Reduce number of joins in mysql

I have 12 fixed tables (group, local, element, sub_element, service, ...), each table with different numbers of rows.
The columns 'id_' in all table is a primary key (int). The others columns are of datatype varchar(20). The maximum number of rows in these tables are 300.
Each table was created in this way:
CREATE TABLE group
(
id_G int NOT NULL,
name_group varchar(20) NOT NULL,
PRIMARY KEY (id_G)
);
|........GROUP......| |.......LOCAL.......| |.......SERVICE.......|
| id_G | name_group | | id_L | name_local | | id_S | name_service |
+------+------------+ +------+------------+ +------+--------------+
| 1 | group1 | | 1 | local1 | | 1 | service1 |
| 2 | group2 | | 2 | local2 | | 2 | service2 |
And I have one table that combine all these tables depending on user selects.
The 'id_' come from fixed tables selected by the user are recorded into this table.
This table was crate in this way:
CREATE TABLE group
(
id_E int NOT NULL,
event_name varchar(20) NOT NULL,
id_G int NOT NULL,
id_L int NOT NULL,
...
PRIMARY KEY (id_G)
);
The tables (event) look like this:
|....................EVENT.....................|
| id_E | event_name | id_G | id_L | ... |id_S |
+------+-------------+------+------+-----+-----+
| 1 | mater1 | 1 | 1 | ... | 3 |
| 2 | master2 | 2 | 2 | ... | 6 |
This table get greater each day, an now it has about thousunds of rows.
Column id_E is the primary key (int), event_name is varchar(20).
This table has, in addition of id_E and event_name columns, 12 other columns the came from the fixed tables.
Every time than I need to retrieve information on the event table, to turn more readable, I need to do about 12 joins.
My query look like this where i need to retrieve all columns from table event:
SELECT event_name, name_group, name_local ..., name_service
FROM event
INNER JOIN group on event.id_G = group.id_G
INNER JOIN local on event.id_L = local.id_L
...
INNER JOIN service on event.id_S = service.id_S
WHERE event.id_S = 7 (for example)
This slows down my system performance. Is there a way to reduce the number of joins? I've heard about using Natural Keys, but I think this is not a good idea to form my case thinking in future maintenance.
My queries are taking about 7 seconds and I need to reduce this time.
I changed the WHERE clause and this caused not affect. So, I am sure that the problem is that the query has so many joins.
Could someone give some help? thanks a lot...
MySQL has a great keyword of "STRAIGHT_JOIN" and might be what you are looking for. First, each of your lookup tables (id/description) I have to assume already have an index on the ID column since that is primary key.
Your event table is the one you are querying as the primary basis of the details and joining to the lookups per their respective IDs. As long as your WHERE clause applicable to the EVENT table is optimized, such as the ID you are looking for, it SHOULD be virtually instantaneous.
If it is not, then it might be that MySQL is trying to think for you and take one of the secondary lookup tables and make it a primary basis of the query for whatever reason, such as much lower record count. In this case, add the keyword and try it..
SELECT STRAIGHT_JOIN ... rest of your query
This tells MySQL to do the query in the order you gave it, thus the Event table first and it's where clause on the ID. It should find that one thing, then grab all the corresponding lookup descriptions from the other tables.
Create indexes, concretely use compound indexes, for instance, start creating a compound index for event and groups:
on table events create one for (event id, group id).
then, on the group table create another one for the next relation (group id, local id).
on local do the same with service, and so on...

For each set of keywords in one table, find all matching hits in a second table

Disclaimer: I'm using MySQL with 2 tables. So far I've found solutions to my issue when individual groups are queried one at a time using IN() but nothing that allows me to do the whole table at once without looping over multiple queries.
I have two tables:
CREATE TABLE WordGroups (
wgId int NOT NULL AUTO_INCREMENT,
groupId int NOT NULL,
word varchar(255) NOT NULL,
PRIMARY KEY (wgId)
);
Which keeps track of groups of keywords, word to groupId, and
CREATE TABLE ArticleWords (
awId int NOT NULL AUTO_INCREMENT,
articleId int NOT NULL,
word varchar(255) NOT NULL,
PRIMARY KEY (awId)
);
which keeps track of the key words within an article.
I am attempting to build a single query which can take the groups of words, and return for each group all articles which contain AT LEAST all of those words.
I realize if I look for one group at a time in a single query this is very simple, however I can't seem to figure out how to make a single query result in the set of all matching subsets.
For example imagine that the two tables have the following data:
WordGroups
groupId | word
-----------------
1 | B
1 | A
2 | C
2 | E
3 | F
ArticleWords
articleId | word
-----------------
1 | A
1 | C
1 | B
2 | C
3 | A
3 | B
3 | F
4 | C
4 | E
4 | F
The resulting query would return:
groupId | articleId
1 | 1
1 | 3
2 | 4
3 | 3
3 | 4
Since those articles contain at least all the words from those groups.
I've attempted an intersection of the two tables using an inner join but that matches incomplete groups of words resulting in the row:
groupId | articleId
2 | 2
Showing up in the result all because Article 2 contains the word "C". I'm open to ideas as I've dabbled in less serious MySQL but this has been eluding me all week.
Any help is much appreciated. I'm at the point where I'm wondering if I'm trying to make SQL do something it isn't meant to do. I have a very long query which works for WordGroups up to 6 words but it is very exact and not scalable, this query would need to work for any size WordGroup to be feasible.
Thank you for reading!
Here is one method, uses group_concat() for the comparison:
select wg.groupId, aw.articleId
from articlewords aw join
wordgroups wg
on wg.word = aw.word join
(select wg.groupId, group_concat(wg.word order by word) as words
from word_groups wg
group by wg.groupId
) wgw
on wgw.groupId = wg.groupid
group by aw.articleid, wgw.words
having group_concat(aw.word order by aw.word) = wgw.words;
Here is a SQL Fiddle.

MySQL - how to optimize large set of conditions

My head is already spinning from this and I need your help.
MY DATABASE
imported CSV file: 22 columns and 11k rows
2 tables with the same data (both created from the CSV)
Added ID as PRIMARY KEY to both
All VARCHAR(60) Some columns are empty strings ' '
DB:
PID | CODE 1 | CODE 2 | CODE 3 | CODE 4 | CODE 5 | CODE X (up to 9) | ID
-------------------------------------------------------------------------
1 | a | b | c | | | | 1
2 | a | | b | d | | | 2
3 | x | | | | | y | 3
DB has 22 columns but I'm only including CODE columns (up to 9)
in which I might be interested in terms of SQL statement.
It'll be only read table - MyISAM engine then?
WHAT I'D LIKE TO DO
select PID = 1 from first table
and retrieve all PIDs from second table
IF
selected PID's column CODE 1
or
selected PID's column CODE 2 (which is b) etc (up to 9).
= any PID's CODE X
So I should get only PID 2.
edit: PID is not a ID, it's just an example code, it could be string: '002451' and I'm looking for other PIDs with the same CODES (e.g PID1 has code = a so it should find PID2 becasue one of its CODE columns contains a)
MY ATTEMPT
SELECT a.* FROM `TABLE1` a WHERE
(
SELECT * FROM `TABLE2` b WHERE b.`PID` = 1
AND
(
( b.`CODE 1` NOT IN ('') AND IN (a.`CODE 1`,a.`CODE 2`, A.`CODE 3`...) ) OR
( b.`CODE 2` NOT IN ('') AND (a.`CODE 1`,a.`CODE 2`, A.`CODE 3`...) ) OR...
I'd end up with large query - over 81 conditions. In terms of performance... well, it doesn't work.
I intuitively know that I should:
use INDEXES (on CODE 1 / CODE 2 / CODE 3 etc.?)
use JOIN ON (but I'm too stupid) - that's why I created 2 tables (let's assume I don't want TEMP. TABLES)
How to write the SQL / design the DB efficently?
The correct data structure is one row per pid and code. The simplest way is:
create table PCodes (
pid int not null,
code varchar(255),
constraint fk_PCodes_pid references p(pid)
);
Then you have the values in a single column and it is much simpler to check for matching codes.
In practice, you should have three tables:
create table Codes (
CodeId int not null auto_increment primary key,
Code varchar(255)
);
create table PCodes (
pid int not null,
codeid int not null,
constraint fk_PCodes_pid references p(pid),
constraint fk_PCodes_codeid references codes(codeid);
);
If the ordering of the codes is important for each "p", then include a priority or ordering column in the PCodes table.

SQLYOG - SQL - Merging two columns into 1 column

I have two columns displaying the same type of information but not necessarily the same data. Although some of the data overlaps each column may/may not contain information that will also include NULL values. Like so:
Company ID | Company Name | Company ID | Company Name
-----------+--------------+------------+-------------
1 | A | 1 | A
2 | B | NULL | NULL
NULL | NULL | 3 | C
I am trying to merge columns 1 and 2 to columns 3 and 4, respectively, so that I have two columns that look like this:
Company ID | Company Name
-----------+-------------
1 | A
2 | B
3 | C
Looking at similar stackoverflow questions, I have doubt this may be done easily. Is this possible? Please, let me know!
Anything helps.
As you don't seem to be around to answer questions for clarification right now, let's go ahead.
It seems, you do actually have the four columns in question in a single table - but than, there should be no duplicate column names. Once they are unique, the following should work:
UPDATE SomeTable
SET company_ID_1 = IFNULL(company_ID_1, company_ID_2)
, company_Name_1 = IFNULL(company_Name_1, company_Name_2)
WHERE
company_ID_1 IS NULL
OR
company_Name_1 IS NULL
;
If the presented is actually the output of a join, you could replace the same by:
SELECT
IFNULL(SomeTable1.company_ID, SomeTable2.company_ID) company_ID
, IFNULL(SomeTable1.company_Name, SomeTable2.company_Name) company_Name
FROM SomeTable1
LEFT JOIN SomeTable2
ON SomeTable1.company_ID = SomeTable2.company_ID
UNION ALL
SELECT
IFNULL(SomeTable1.company_ID, SomeTable2.company_ID) company_ID
, IFNULL(SomeTable1.company_Name, SomeTable2.company_Name) company_Name
FROM SomeTable1
RIGHT JOIN SomeTable2
ON SomeTable1.company_ID = SomeTable2.company_ID
WHERE SomeTable1.company_ID IS NULL
ORDER BY company_ID
;
See it in action: SQL Fiddle
Please comment, if and as this requires adjustment / further detail.