MySQL - Searching in a multiple value field - php

In my database table I have the following fields:
Table Supplier:
id
name
vehicles
A supplier can have multiple vehicles. The field 'vehicles' will store multiple values. At the moment I am delimiting the values on a 'pipe' symbol, although this can be changed to a comma if need be.
On my front-end form I have a checkbox list - a user can select multiple vehicles. The back end script needs to do a search and bring back all suppliers that contain any of the specified vehicle id's.
So in other words we are searching with multiple values in a multiple value field.
The checkbox list name is vehicle_type[] and will end up in the $_POST array as (for example):
Array
(
[0] => 1
[1] => 4
[2] => 6
)
Is this possible to do? I could obviously do this using a join table but ideally I would like to do it this way. I am using PHP as my scripting language if that helps.

The field 'vehicles' will store multiple values. At the moment I am delimiting the values on a 'pipe' symbol, although this can be changed to a comma if need be.
Please don't do that. Storing delimited data in a text field is no better than storing it in a flat file. The data becomes unqueryable.
You want a nice, happy, normalized database.
CREATE TABLE Suppliers (
supplier_id INTEGER PRIMARY KEY,
...
);
CREATE TABLE Vehicles (
vehicle_id INTEGER PRIMARY KEY,
...
);
CREATE TABLE Supplier_Vehicles (
supplier_id INTEGER NOT NULL REFERENCES Suppliers(supplier_id),
vehicle_id INTEGER NOT NULL REFERENCES Vehicles(vehicle_id),
UNIQUE KEY(supplier_id, vehicle_id)
);
-- Grab all the Vehicles for Supplier 39
SELECT Vehicles.*
FROM Vehicles, Supplier_Vehicles
WHERE Supplier_Vehicles.supplier_id = 39
AND Supplier_Vehicles.vehicle_id = Vehicles.vehicle_id
The checkbox list name is vehicle_type[] and will end up in the $_POST array as (for example) [...] Is this possible to do?
It's very possible, and is a good idea. Try it and find out how well it works.

Related

Query data values where keys are different

I am having a little bit logic issue with querying data.
I have table with key and value columns
key has different values and that values stores data in valuecolumn
Example:
**key** **value**
car_name Honda
car_color black
car_name Audi
car_color white
.... .....
.... .....
I am able to select all car names or car colors
SELECT DISTINCT(value) as name FROM `cars` WHERE key = 'car_name';
SELECT DISTINCT(value) as color FROM `cars` WHERE key = 'car_color';
BUT, how can I combine these two values?
E.g
if car_name = Honda show only colors that Honda has in DB stored as car_color (in example case it would be black)
Current output
showing of course all the values under car_color (in example case it would be black, white)
Is it possible to combine these two keys/values?
At least you must have another column to cater your requirement. So i added a column and created table for a simple explanation. But your system need a good structure to archive your target.
We can add the object names into the new column we introduced. honda1 and Audi1 mean your object names. (Example )
If your unable to add a column, definitely this is not the answer for you and your requirement also impossible.
Then you can use the query like below
SELECT * FROM key_values WHERE cat IN (
SELECT cat FROM `key_values` WHERE `value` = 'honda')
AND `key` = 'car_color'
Note: here i added a 'key' and 'value' as column names, but not recommend to do with your live system.

Storing an random size array in a mySQL column

I have a table. It cointains two columns unique song and genre. Unique song stores a string, doesn't really matter what.
Genres contains an array of strings (genres that apply to the song), number of elements in the strings being random (to big to ditch the array and just make additional columns).
I know that this setup does not work in mySQL as I set it up, but that is what I need.
One way to do it would be serialization, but I would very much like to be able to query out all rock songs without having to first querying them all, unserializing and then finding my match.
Since all of the array contents are of the same type, is there a column that would support such an input? (int is a limited array of ints in a way, no?)
You've got a many-to-many relationship - one song can have multiple genres, and a genre can be used by multiple songs.
Create a table called Song, that contains information about the song and some unique identifier. For the sake of argument, we'll just say it's the name of the song: s_name.
Create a table called Genre, that contains information about genres. Maybe you have the genre, and some information on what style of music it is.
Finally, create a table called SongAndGenre, that'll act as a bridge table. It'll have two column - a song ID (in our case, s_name), and a genre ID (say, g_name). If a song S has multiple genres G1 and G2, you'll have two rows for that song - (S, G1) and (S, G2).
You now have a table, let's say, songs, containing a column genres.
To know the genres of song #123, you can now issue
SELECT genres FROM songs WHERE id = 123;
What you need to do is to create two additional tables:
CREATE TABLE genres (
genre_id integer not null primary key auto_increment,
genre_name varchar(75)
);
CREATE TABLE song_has_genre (
song_id integer not null,
genre_id integer not null
);
To store the fact that song 123 is in genres 'Folk', 'Pop', 'Jazz' and 'Whatever', you can run:
INSERT INTO song_has_genre
SELECT 123, genre_id FROM genres
WHERE genre_name IN ( 'Folk', 'Pop', 'Jazz', ... );
To query what songs are in genre Folk,
SELECT songs.*, genres.genre_name FROM songs
JOIN song_has_genre AS shg ON ( songs.id = shg.song_id )
JOIN genres ON (shg.genre_id = genres.genre_id)
WHERE genres.genre_name = 'Folk';
A bit more work is needed to avoid duplicates if you select two genres and one song is in both, or to retrieve all genres of some songs selected based on genre (i.e., you search 'Pop', and want to find 'Pop,Jazz,Folk', 'Pop,Techno', 'Pop', 'Pop,Whatever', but not 'Techno,Jazz,Folk,Anything except Pop'), but it's doable (e.g. using GROUP_CONCAT and/or GROUP BY, or in the code outside MySQL).

DB Design; or Conditional Selects with json data

I have a DB with several tables that contain basic, static ID-to-name data. 2 Columns only in each of these reference tables.
I then have another table that will be receiving data input by users. Each instance of user input will have it's own row with a timestamp, but the important columns here will contain either one, or several of the ID's related to names in one of the other tables. For the ease of submitting and retrieving this information I opted to input it as text, in json format.
Everything was going great until I realized I'm going to need to Join the big table with the little tables to reference the names to the ID's. I need to return the IDs in the results as well.
An example of what a few rows in this table might look like:
Column 1 | Column 2 | Timestamp
["715835199","91158582","90516801"] | ["11987","11987","22474"] | 2012-08-28 21:18:48
["715835199"] | ["0"] | 2012-08-28 21:22:48
["91158582","90516801"] | ["11987"] | 2012-08-28 21:25:48
There WILL be repeats of the ID#'s input in this table, but not necessarily in the same groupings, hence why I put the ID to name pairings in a separate table.
Is it possible to do a WHERE name='any-of-these-json-values'? Am I best off doing a ghetto join in php after I query the main table to pull the IDs for the names I need to include? Or do I just need to redo the design of the data input table entirely?
First of all:
Never, ever put more than one information into one field, if you want to access them seperately. Never.
That said, I think you will need to create a full N:M relation, which includes a join table: One row in your example table will need to be replaced by 1-N rows in the join table.
A tricky join with string matching will perform acceptably only for a very small number of rows, and the WHERE name='any-of-these-json-values' is impossible in your construct: MySQL doesn't "understand", that this is a JSON array - it sees it as unstructured text. On a join table, this clause comes quite naturally as WHERE somecolumn IN (1234,5678,8012)
Edit
Assuming your Column 1 contains arrays of IDs in table1 and Column 2 carries arrays of IDs in table2 you would have to do something like
CREATE TABLE t1t2join (
t1id INT NOT NULL ,
t2id INT NOT NULL ,
`Timestamp` DATETIME NOT NULL ,
PRIMARY KEY (t1id,t2id,`Timestamp`) ,
KEY (t2id)
)
(you might want to sanity-check the keys)
And on an insert do the following (in pseudo-code)
Remember timestamp
Cycle all permutations of (Column1,Column2) given by user
Create row
So for your third example row, the SQL would be:
SELECT #now:=NOW();
INSERT INTO t1t2join VALUES
(91158582,11987,#now),
(90516801,11987,#now);

SQL return only not empty columns from row as new row

I'm in the situation where my client e-mails me an excel-file with 50 columns of data extremely un-normalized. I then export it to CSV and upload into MySQL -- single table. The columns are for different ingredients (10 columns of data for each ingredient -- title, category, etc) and then 40 different columns for characteristics on each ingredients. So each ingredient in the table has all of these 50 columns even though every column doesn't apply for that ingredient.
My question is if I can create a SQL that selects only filled in characteristics for one selected ingredient and leaves out all of the other columns?
(I know that another option is to build my own CSV-parser that created multiple tables and then write SQL for them instead, but I wanna investigate solving this as is first. If that's not possible then I just have to face that and build a parser ;P)
This is as far as I came but this doesn't completely exclude columns not filled in (or that contains "nei".
SELECT
IF(`Heving-vanlig-gjaerbakst` <> '' AND `Heving-vanlig-gjaerbakst` <> 'nei', `Heving-vanlig-gjaerbakst`, 'random') AS `test1`,
IF(`Frys-kort` <> '' AND `Frys-kort` <> 'nei', `Frys-kort`, 'random') AS `test2`
... and for the 38 other rows ...
FROM x
WHERE id = 123
And I'd rather not solve this in the PHP-code by skipping empty rows =P
Example row (column names first):
g1 gruppe ug1 undergruppe artnr artikkel beskrivelse status enhet ansvar prisliste Heving-vanlig-gjaerbakst Heving-soete-deiger Deig-stabilitet Smaksgiver Saftighet Krumme-poring Skorpe Volum Konservering Skjaerbarhet Frys-lang Frys-kort Kjoel Holdbarhet E-fri Azo-fri Mandler Aprikoskjerner Helmiks Halvmiks Base Konsentrat Utstrykning Bakefasthet Frukt-Baerinnhold Slippegenskaper Hindre-koksing Palmefri Fritering Smidighet Baking Kreming Roere Fylning Dekor Prefert Viskositet Cacaoinnhold Fet-innhold
100150 Bakehjelpemidler 100150200 Fiber/potetprodukter 10085 Potetflakes sekk 15 kg Egnet til lomper, lefser, brød og annet bakverk. B... Handel Sekk Trond Olsen JA xxx xxx xxx
As you can see most columns are empty here. X, XX and XXX is a form of grade-system, but for some columns the content is instead "yes" or "no".
And as I said, the first 10 columns are information about that product, the other 40 is different characteristics (and it's those I wanna work with for one given product).
It sounds a bit as if you'd like to convert the table you have into two tables:
CREATE TABLE Ingredients
(
g1 ...,
gruppe ...,
ug1 ...,
undergruppe ...,
artnr ... PRIMARY KEY,
artikkel ...,
beskrivelse ...,
status ...,
enhet ...,
ansvar ...,
prisliste ...
);
I've opted to guess that the artnr is the primary key, but adapt what follows to the actual primary key. This table contains the eleven (though your question said ten) columns that are common to all ingredients. You then have another table which contains:
CREATE TABLE IngredientProperties
(
artnr ... NOT NULL REFERENCES Ingredients,
property VARCHAR(32) NOT NULL,
value VARCHAR(3) NOT NULL,
PRIMARY KEY(artnr, property)
);
You can then load the populated columns from your original table into these two. At worst, there'd be 40 entries in IngredientProperties for one entry in Ingredient. You might make 'property' into a foreign key reference to a defining list of possible ingredient properties (a third table that defines the possible values for the properties - basically, a record of the column names from your original table). If you add the third table, it might logically be called IngredientProperties (too), in which case the table I called IngredientProperties needs to be renamed.
You can then join Ingredients and IngredientProperties to get the information you want.
I'm not sure that I recommend this solution; it is basically a use of the 'Entity Attribute Value' approach to database design. However, for extremely sparse information like you seem to have, and when used with the constraint of the third table.
What you can't sensibly do is handle all possible combinations of 40 columns as that number grows exponentially with the number of columns (and is pretty large with N = 40).

Finding value in a comma-separated text field in MySQL?

I've got a database of games with a genre field that has unique ids in it that are separated by commas. It's a text field. For example: HALO 2 - cat_genre => '1,2' (Action,Sci-Fi)
I'm trying to make a function that calculates the total number of games in that genre. So it's matching 1 value to multiple values separated by commas in the db.
I was using SELECT * FROM gh_game WHERE cat_genre IN (1) which would find Action Games.
I'm trying to work this out, I thought I had nailed it once before but I just can't figure it out.
You need to create a many to many relation. like so
CREATE TABLE gameGenreTable ( id int NOT NULL PRIMARY KEY, genreID, gameID)
EDIT: if you're using InnoDB you can also create foreign keys on genreID and gameID..
I would add a UNIQUE key on genreID, gameID
then you can do a query like this
SELECT genreID,count(genreID) as count from gameGenreTable GROUP BY genreID;
-- and join in your other table to get the genre name (or just use the ID).

Categories