How to convert string value separated by comma into integer from mySQL - php

I am working on a php project to retrieve data from mysql. I have a list of codes that is stored as a string separated by commas that is referenced in another table. Is there a way to get all the value from the string and return the text it referenced?
For example, item_purchased might contain one or more than one item_code. I want the query to return item names instead of item codes.
//item_purchased for transaction 123 --> ,111,222,333,
SELECT s.transaction_id, s.item_purchased, i.item_id
FROM stock s
INNER JOIN ref_item i
ON s.item_code = i.item_code
WHERE transaction_id = 123
Desired outcome: apple, carrot, milk (not ,111,222,333,)
Is there a way to do this preferably within mySQL query or maybe in PHP?

This is one of the reasons why you shouldn't use comma-separated lists in relational databases.
The workaround is to use the FIND_IN_SET() function in the join conditions, instead of =.
SELECT s.transaction_id, GROUP_CONCAT(i.item_name)
FROM stock s
INNER JOIN ref_item i
ON FIND_IN_SET(i.item_code, s.item_purchased)
WHERE s.transaction_id = 123
GROUP BY s.transaction_id
But unfortunately, this makes the query very inefficient, because it can't use an index to search the ref_item table. It has to do a table-scan, so it ends up having very poor performance, and gets much worse the larger your table gets.

Is item_purchased a comma separated string of item_code?
I'm not good with joins but I think this will do
SELECT s.transaction_id, s.item_purchased, DISTINCT(i.item_id)
FROM stock s, ref_item i
WHERE i.item_code in (s.item_purchased )
AND s.transaction_id = 123
This will return a list of all the items in the item_purchased column if my assumption of the item_purchase is right.

Related

How to check if a DB array matches any values of a given array

I'm a little bit stuck.
I have an SQL column that contains weather codes (like Rain, Snow, etc.)
It is comma separated so the column would have a value of something like
rain,snow,haze
Now, I want to select the rows that contain values from an array.
I have an SQL code that is something like this:
SELECT * FROM locations WHERE currentWeather IN ('rain', 'snow', 'cloudy') ORDER BY name ASC
The problem is that this obviously works when currentWeather column only contains one item.
Is there a way to do it so that if the column value contains any of the items from the given array, it selects it?
Also, would it select it twice if two items match?
Best wishes
Use unnest in a subselect.
Select distinct A.myArray from (select unnest(column) as myArray from table) A where A.myArray in (your words to filter for)
Notice that using arrays in sql isn't very ideal and does not follows normalization rules. Your tables should ideally not contain arrays but rather just several rows each one containing the specific value you Want. It prevents issues such as this one.
To avoid the selection of repeated values, use the Distinct keyword right after you write select.
Rsference:
https://www.w3resource.com/PostgreSQL/postgresql_unnest-function.php
WHERE FIND_IN_SET(currentWeather, "rain,snow,cloudy")
Picks apart the string at commas (only) to see if currentWeather is any one of those 3 'words'.
See also FIELD(...)

Joining MySQL tables and comparing columns to list data

I have been struggling with the proper way to extract the data I need. I am using MySQL with PHP and will be putting the data into a list format. My only problem is with the actual query itself. Here is how the DB is setup:
I have a 'chars' table for characteristics with the columns 'id', 'descrip', and 'class'.
I have a 'animal' table with the columns 'animal_id', 'charlist', ...
In 'chars' id is an incremented int, 'descrip' and 'class' are text/strings.
'id' and 'descrip' are different for every row but 'class' will be the same sometimes having a value like 'habitat', 'size', 'diet', et cetra.
What I am going for is a list that will look like this:
Habitatundergroundarctic
Dietfishinsectsomnivore
Here is where I start to have trouble. On a 'Details' page I am showing all of the data specific to a certain animal (whichever is clicked) and to reference the 'characteristics' each animal has it's own 'charlist' value in the database. This value is a string of numbers that reference the 'chars' table, like '2,55,67,90,122'.
So I've pulled all my (specific) animal data to a php variable on the page that I want to supply with the information. Now I need to use the 'charlist' data from that animal to lookup and list the characteristics relative to the animal.
My queries are looking like this, and I know I'm waaay off:
SELECT * FROM 'chars' LEFT INNER JOIN 'animals' ON chars.id IN (animals.charlist) WHERE ...
I've tried a lot of different ways and this is where I get lost. My brain is telling me to join the tables, find the numbers that are in the charlist WHERE animal.animal_id = mysql_real_escape_string($animal_id) - and of course MySQL tells me I can't do it this way.
I know that for the titles of the lists I'll probably have to do this with a GROUP BY condition.
Any help on the query and query syntax would be great. I'm fairly new to MySQL and I'm very happy and eager to learn how to do this right.
Thanks for reading.
a IN (x) returns true if (and only if) a equals x.
If x is a string (even one delimited by commas), then the statement will only be true if a is equal to that same string. That is, 123 IN ('123,456,789') is false. Note that this is not the same as passing multiple arguments such as 123 IN (123, 456, 789).
In MySQL, one could instead use FIND_IN_SET(), which expects a string delimited by commas. However, storing delimited lists in database columns is a really bad idea. You should instead define a third, relationship, table in which you store foreign keys into both of your existing tables: (animal_id, characteristic_id):
CREATE TABLE animal_characteristics (
PRIMARY KEY (animal_id, characteristic_id),
FOREIGN KEY (animal_id) REFERENCES animal (animal_id),
FOREIGN KEY (characteristic_id) REFERENCES chars (id)
) SELECT animal.animal_id, chars.id AS characteristic_id
FROM animal JOIN chars ON FIND_IN_SET(chars.id, animal.charlist)
;
Then you can do:
SELECT *
FROM animals
JOIN animal_characteristics USING (animal_id)
JOIN chars ON chars.id = animal_characteristics.characteristic_id
WHERE ...

handling a lot of data with mysql and php in search

I'm making a car part system, to store all the parts inside mysql and then search for them.
Part adding goes like this:
you select up to 280 parts and add all the car info, then all the parts are serialized and put into mysql along with all the car info in a single row.
(for this example I'll say that my current database has 1000 cars and all of those cars have 280 parts selected)
The problem is that when I have 1000 cars with each of them having 280 parts, php and mysql starts getting slow and takes a lot of time to load the data, because the number of parts is 1000*280=280 000.
I use foreach on all of the cars and then put each part into another array.
The final array has 280 000 items and then I filter it by the selected parts in the search, so out of 28 000 parts it may have only have to print like 12 500 parts (if someone is searching for 50 different parts at the same time and 250 cars have that part).
Example database: http://pastebin.com/aXrpgeBP
$q=mysql_query("SELECT `id`,`brand`,`model`,`specification`,`year`,`fueltype`,`capacity`,`parts`,`parts_num` FROM `warehouse`");
while($r=mysql_fetch_assoc($q)){
$partai=unserialize($r['parts']);
unset($r['parts']); //unsetting unserialized parts so the whole car parts won't be passed into the final parts-only array
foreach($partai as $part){
$r['part']=$parttree[$part]; //$parttree is an array with all the part names and $part is the part id - so this returns the part name by it's id.
$r['part_id']=$part; // saves the part id for later filtering selected by the search
$final[]=$r;
}
}
$selectedparts=explode('|', substr($_GET['selected'], 0,strlen($_GET['selected'])-1)); //exploding selected part ids from data sent by jquery into an array
foreach($final as $f){
if(in_array($f['part_id'], $selectedparts)){
$show[]=$f; //filtering only the parts that need to be shown
}
}
echo json_encode($show);
This is the code I use to all the cars parts into arrays and the send it as json to the browser.
I'm not working on the pagination at the moment, but I'll be adding it later to show only 10 parts.
Could solution be to index all the parts into a different table once 24h(because new parts will be added daily) and then just stressing mysql more than php? Because php is doing all the hard work now.
Or using something like memcached to store the final unfiltered array once 24h and then just filter the parts that need to be shown with php?
These are the options I considered, but I know there must be a better way to solve this.
Yes, you should definitely put more emphasis on MySQL. Don't serialize the parts for each car into a single row of a single column. That's terribly inefficient.
Instead, make yourself a parts table, with columns for the various data items that describe each part.
part_id an autoincrement item.
car_id which car is this a part of
partnumber the part's external part number (barcode number?)
etc
Then, use JOIN operations.
Also, why don't you use a WHERE clause in your SELECT statement, to retrieve just the car you want?
Edit
If you're looking for a part, you definitely want a separate parts table. Then you can do a SQL search something like this.
SELECT w.id, w.model, w.specification, w.year, w.fueltype,
p.partnumber
FROM warehouse w
JOIN parts p ON (w.id = p.car_id)
WHERE p.partnumber = 'whatever-part-number-you-want'
This will take milliseconds, even if you have 100K cars in your system, if you index it right.
Your query should be something like:
<?php
$selectedparts=explode('|', substr($_GET['selected'], 0,strlen($_GET['selected'])-1)); //exploding selected part ids from data sent by jquery into an array
$where = ' id < 0 ';
foreach ($selectedparts AS $a){
$where .= " OR `parts` like '%".$a."%'";
}
$query = "SELECT * FROM `warehouse` WHERE ".$where." ORDER BY `id` ASC";//this is your query
//.... rest of your code
?>
Yes, look into has many relationships a car has many parts.
http://net.tutsplus.com/tutorials/databases/sql-for-beginners-part-3-database-relationships/
Then you can use an inner join to get the specified parts. You can do a where clause to match the specific partIds to filter out unwanted parts or cars.

Better way of getting values from table without looping 300 queries

I have a query and a loop written that lists all the rows from a mysql table ("records") formatted in a HTML table.
One of the fields is "subject_id" which is an integer. I also have a table named "subjects" which corresponds to the same "subject_id" in the records table. Only two fields are in the "subjects" table, is an auto-index ID integer and a varchar(1024) for the subject title.
The value that returns from the "records" table is an integer. I want to perform a lookup on the integer from the "records" table for each row to output the text equivalent from the "subject_id" field.
My first notion, the kindergarten way, would be to throw in another query within the loop, effectively increasing my number of queries from 300 to 600 to load the page (no pagination is needed).
What would be a better way of this "sub query" aside from adding 300 more queries?
Edit:
I'm pulling the data from the table using a while loop and echoing my variable $r_subject (from: $r_subject = mysql_result($result,$a,"subject");). The value returned from the initial records table is INT. I want to take the $r_subject and then check it against the SUBJECTS table to get the string name associated with that INT id.
It's hard to know exactly what you need without seeing code, but from what I gather, you have 2 tables, one has the ID, the other has the text, so you would want to use a join.
The second thing is, you'll want to look at whether or not you really need 300 queries in the first place. That's a lot of queries to run and you should only need to run that many queries when you're running a bulk insert/update or something of that nature; other than that, you most likely could reduce that number substantially.
select
A.*,
B.title
from
records A,
subjects B
where
B.subject_id = A.subject_id
That's a single query that will produce all of the data you need for your page.
select
subjects.SubjectTitle,
records.whateverFieldYouWant,
records.AnyOtherField
from
records
join subjects
on records.subject_id = subjects.subject_id
where
records.subject_id = TheOneSubjectYouWant
but can't confirm without actual structure of tables and some sample data displayed showing proper context of what you are expecting out

MySQL Query Where Column Like Column

I'm working on a small project that involves grabbing a list of contacts which are stored for each group. Essentially, the database is set up so that each group has a primary and secondary contact stored as, unsurprisingly, Group.Primary and Group.Secondary. The objective is to pull every Primary and Secondary contact for each Group and display them in a sortable table.
I have the sortable table all worked out, but I have come across a small problem. Each primary and secondary field can have more than one contact separated by a comma. For instance, if Primary contained 123,256 , it would need to pull both Contacts with IDs 123 and 256. I had intended to use a query formatted like this:
SELECT *
FROM Group G,
Contacts C
WHERE G.Primary LIKE %C.ID%
OR G.Secondary LIKE %C.ID%
so that I could just skip the comma part, but I can't seem to find a working query for this.
My question to you is, am I just overlooking something here? Is there a simple query that would let me do this? Or am I better off getting the groups and contacts separately, and combine the two later. I think the former is a little easier to understand when read, which is a plus as this is a shared project, but if that is not possible I will do the latter.
This code is simplified, but it gets the point across.
If I understand correctly, you want to use the MySQL FIND_IN_SET function:
SELECT *
FROM Group G
JOIN Contacts C ON FIND_IN_SET(c.id, g.primary)
OR FIND_IN_SET(c.id, g.secondary)
But I highly recommend you normalize the table -- do not store comma delimited lists if at all possible.
I think you're definitely better off separating those two data values into different tables and then using JOINs to do your linking. If you were to, say, cast your id fields to strings so you could use the LIKE comparison, you'd end up with a bunch of junk matches. For example, if your primary id is 1, and your secondary is 35, then you'd match on the following (and this list is not exhaustive):
1: 1, 2: 35
1: 35, 2: 1
1: 10, 2: 135
1: 431, 2: 3541
etc.
What I'd do instead is something like this:
SELECT *
FROM Group G
LEFT JOIN Contacts c1 on g.primary = c1.id
LEFT JOIN Contacts c2 on g.secondary = c2.id
WHERE
c1.id IS NOT NULL
OR
c2.id IS NOT NULL
I think that'll get you the data you're really looking for, if I understand the question correctly.
Satan has been in your database, denormalizing it and dooming you to a life of complex and slow queries.
Do you have the ability to alter the structure of the database? If it's in production, I assume not. Failing that, you might want to consider creating a normalized table of primary and secondary contacts immediately prior to running this report.
If you can't do that, you need to work out a string matching algorithm that will always work. The problem with the one that you proposed is that you need to consider a contact id of 23 (or even 3), which will match 23, 123, 223, 231, and so on. To make that work, you need to add commas to the beginning and ending of both strings you're comparing and then do the LIKE.
Oops. Or you can use the I-never-knew FIND_IN_SET function described by Ponies, above.

Categories