Can I have a loop within an SQL SELECT statement? - php

I'm trying to create an SQL SELECT statement on my PHP file which can select all rows that have an unknown number of different values in one of the column. To explain better I'll say the exact situation. I have three tables in my database; tests, categories, sections. Each test belongs to a category, there are around 50 categories, each category belongs to a section, there are 10 sections. However, in my tests table, it only has a column for the category it belongs to. I now want to be able to display all tests within a section.
So to make things clearer:
SELECT * FROM tests WHERE category = '$categoryId' ORDER BY id ASC
This lets me select all tests in a particular category, but I want to make a loop of some sort, where, depending on which section is selected, the category id's are put in this statement separated by OR. Thank you.

SELECT t.*
FROM tests t
JOIN category c on t.category = c.id AND c.section = ' . $section . '
ORDER BY t.id ASC
This is to show you the way, in your production environment you should sanitize the $section parameter.
The idea is to join tables together to take advantage of the powerful relational database features over programmatic loops.
Internally, the RDBMS is doing loops for you, calculating intermediate resulset of each JOINed table.

If you want to select all tests in one section then you should not use the or statement on category id but should use join on these tables:
something like:
SELECT tests.* FROM (tests JOIN categories ON tests.category = categories.id) WHERE categories.section = $section
where $section is the id of section you want to select tests.
You can also specify columns to use instead of * to select only those you are interested in.
$section should be escaped to be sure that it cannot be injected.
I do not know your table schema but when you have relations like these you should have foreign keys in these tables and the query should be fast and you need only one! This is how to perform queries on data model as relations are stored right in the DB.

use special character for Example pipe symbole |
and do this :
$str="|".$cat1."|".$cat2."|".$cat3+"|";
and use Like condition :
SELECT * FROM tests WHERE $str like '%|'+cast(category as varchar) +'|%' oRDER BY id ASC

Related

MYSQL JOIN Conditional to check integer against comma delimited list

I have a SQL SELECT statement in which I'm using 3 tables.
I'm using INNER JOINs to join the tables, however I've come across a bit of an issue because two of the columns that I'd like the join conditional to be based on are different data types;
One is an integer - the id of the products table and can be seen below as p.id.
The other is a comma delimited string of these id's in the order table. customers can order more than one product at a time, so the product id's are stored as a comma delimited list.
here's how far I've gotten with the SQL:
"SELECT o.transaction_id, o.payment_status, o.payment_amount, o.product_id, o.currency, o.payment_method, o.payment_time, u.first_name, u.last_name, u.email, p.title, p.description, p.price
FROM orders AS o
INNER JOIN products AS p ON ( NEED HELP HERE--> p.id IN o.product_id comma delimited list)
INNER JOIN users AS u ON ( o.user_id = u.id )
WHERE user_id = '39'
ORDER BY payment_time DESC
LIMIT 1";
Perhaps I could use REGEX? currently the comma delimited list reads as '2,1,3' - however the number of characters isn't limited - so I need a conditional to check if my product id (p.id) is in this list of o.product_id?
What you have is a perfect example for one-to-many relationship where you have one order and several items attached to it. You should have a link table like
order_product - which makes the connection between a orderid and productid where you can also put specific data for the relationship between the two (like when the item was added, quantity, etc)
Then you make the join using this table and you have same field types everywhere.
simple example:
select
/* list of products */
from
order o,
order_product op,
product p
where
o.id = 20
and o.id = op.orderid
and op.productid = p.id
This in one of those very common nightmares when working with legacy database.
The rule is simple: never ever store multiple values in one table columns. This is known as first normal form.
But how to deal with that in existing DB?
The good thing™
If you have the opportunity to refactor your DB, extract the "comma separated values" to their own table. See http://sqlfiddle.com/#!2/0f547/1 for a basic example how to do that.
Then to query the tables you will have to use a JOIN as explained in elanoism's answer.
The bad thing™
I you can't or don't want do that, you probably have to rely on the FIND_IN_SET function.
SELECT * FROM bad WHERE FIND_IN_SET(target_value, comma_separated_values) > 0;
See http://sqlfiddle.com/#!2/29eba/2
BTW, why is this bad thing™? Because as you see, it is not easy to write query against multi-valued columns -- but, probably more important, you are not able to use index on that columns, nor, as a consequence, to easily perform join operations or enforce referential integrity.
The so-so thing™
As a final note, if the set of possible value is small (less that 65), an alternative approach would be to change the column type to a SET().

Getting Labels of id's

What is the best way to get labels of id's .
Here is the problem i'm facing .
I have a many tables that only contains id's (subject_id , level_id , place_id , etc..)
What is the best way to display the labels of those id's without making a complex sql query when displaying (have minimum of 6'ids) ?
The other options which is not very nice to do would be to call get_label(id,table,lang)
but of course you can see the problem for each column (Total queries = column * rows)
Any better solution or i'm stuck without doing the join on 6 tables ?
If it's helps i'm using kohana
here is what i have ...
and the subject table for the subject_id :
I have for every field_id a table that correspond .
In term of performance which is better making a join or just calling a query to get the specific label when needed . ?
You want to use a SQL JOIN for this.
SELECT t1.*, t2.subject_en, ...
FROM table1 t1
JOIN table2 t2 ON (t2.id = t1.subject_id)
A JOIN has a much better performane - you have only a single query which can be properly optimized by the database engine while doing a SELECT while iterating over the rows from the initial query would give you n+1 separate queries.

Using PHP, how do I query this mySQL database across these three tables?

I hate to submit a new question, but everyone else has some slight thing that is different enough to make this one seem necessary to ask.
Users are to type in a vendor name, and then see all the "kinds" of things they have bought from that company, in a list, sorted by the lowest-inventory-on-hand.
Summary:
I have three tables.
There are more fields than these, but these are the relevant ones (as far as I can tell).
stuff_table
stuff_vendor_name *(search this field with $user_input, but only one result per lookup_type)*
lookup_type
lookup_table
lookup_type
lookup_quantity (order by this)
category_type
category_table
category_type
category_location (check if this field == $this_location, which is already assigned)
Wordier Explanation:
The users are searching for a value that is contained only in the stuff_table -- distinct stuff_vendor_name values for each lookup_type. Each item can be bought from multiple sources, the idea is to see if any vendor has ever sold even one of any type of item before.
But the results need to be ORDER BY the lookup_quantity, in the lookup_table.
And importantly, I have to check to see if they are searching the correct location for these categories, located in the category_table in the category_location field.
How do I efficiently make this query?
Above, I mentioned the variables that I have:
$user_input (the value we are searching for distinct matches in the stuff_vendor_name field) and $current_location.
To understand the relationship of these tables, I will use an example.
The stuff_table would have dozens of entries with dozens of vendors, but have a lookup_type of, say, "watermelon," "apple," or "cherry."
The lookup_table would give the category_type of "Jellybean." One category type can have multiple lookup_types. But each lookup_type has exactly one category_type.
You are not sharing much about the relationships, but try this:
SELECT *
FROM stuff_table st
LEFT JOIN lookup_table lt
ON st.lookup_type = lt.lookup_type
LEFT JOIN category_table ct
ON lt.category_type = ct.category_type
AND ct.category_location = $this_location
GROUP BY st.lookup_type
ORDER BY lt.lookup_quantity
WHERE st.stuff_vendor_name = $user_input
From a first glance at it you could use foreign keys in your tables to make link between them or using the LEFT JOIN mysql command to make abstraction of another linked table.
The only example I can think of is on a Doctrine pattern, but I think you'll get what I'm saying:
$q = Doctrine_Query::create()
->from('Default_Model_DbTable_StuffTable s')
->leftJoin('s.LookupTable l')
->leftJoin('s.CategoryTable c')
->orderBy('l.lookup_quantity DESC');
$stuff= $q->execute(array(), Doctrine_Core::HYDRATE_ARRAY);
I made a nested query instead.
The final code looks like this:
$query_row=mysql_query(
"SELECT DISTINCT * FROM table_a WHERE
field_1 IN (SELECT field_1 FROM table_b WHERE field_2 = $field_2)
AND field_3 IN (SELECT field_3 FROM table_c WHERE field_4 = $field_4)
ORDER BY field_5 DESC
");
This was incredibly simple. I just didn't know you could do a nested query like that.
I read it was "bad form" because it makes some kind of search optimization not as good as it could be, so be careful using nested select statements.
However for me, it seemed to actually be significantly faster.

PHP MySql query 2 tables that have no common attributes at the same time?

I am trying to query 2 tables in a database, each query having nothing to do with each other, other then being on the same page.
Query 1 - The first query on the page will retrieve text and images that are found throughout the page from Table A.
Query 2 - The second query will retrieve several products with a image, description and title for each product from Table B.
I know that putting the second query inside the first query's while loop would work but of course is very inefficient.
How can I and what is the best way to retrieve all the data I need through 1 query?
Thanks,
Dane
So all you want to know is if its ok to have 2 queries on the same webpage? Its A-OK. Go right ahead. Its completelly normal. No one expects a join between table news and table products. Its normal to usetwo queries to fetch data from two unrelated tables.
Use LEFT or INNER JOIN (depends on whether you want to display records from TableA that have no correspondent records in TableB)
SELECT a.*, b.*
FROM TableA a
[LEFT or INNER] JOIN TableB b ON (b.a_id = a.id)
If there's no way to relate the two tables to each other, then you can't use a JOIN to grab records from both. You COULD use a UNION query, but that presumes that you can match up fields from each table, as a UNION requires you to select the same number/type of fields from each table.
SELECT 'pageinfo' AS sourcetable, page.id, page.images, page.this, page.that
WHERE page.id = $id
UNION
SELECT 'product' AS sourcetable, products.id, products.image, product.other, product.stuff
But this is highly ugly. You're still forcing the DB server to do two queries in the background plus the extra work of combining them into a single result set, and then you have to do extra work to dis-entangle in your code to boot.
It's MUCH easier, conceptually and maintenance-wise, to do two seperate queries instead.

Adding up row number and displaying total using COUNT (PHP MySQL)

I'm attempting to run a query that adds up the total number of subjects in a class. A class has many subjects. There is a 'teachersclasses' table between teachers (the user table) and classes. The principles sounds pretty simple but I'm having some trouble in getting my page to display the number of subjects for each class (directly associated with the teacher)
This is what I have so far, trying to make use of the COUNT with a nested SELECT:
SELECT (SELECT count(*) FROM subjects WHERE subjects.classid = class.classid) AS total_subjects, class.classname, class.classid
FROM class
Then I am calling up 'num_subjects' to present the total within a while loop:
<?php echo $row['total_subjects']?>
From the above, I am receiving the total subjects for a class, but within the same table row (for one class) and my other while loop doesnt run anymore, which returns all of the classes associated with a teacher :( ... Bit of a mess now!
I know to return the classes for a particular teacher, I can do an additional WHERE clause on the session of 'teacherid' but I think my query is getting too complicated for me that errors are popping up everywhere. Anyone have a quick fix for this! Thanks very much
Your query is probably not optimal. It might be a good idea to rewrite it as a join:
SELECT
total_subjects,
class.classname,
class.classid
FROM class
JOIN (
SELECT classid, count(*) AS total_subjects
FROM subjects
GROUP BY classid
) AS T1
ON T1.classid = class.classid
As for your problem, you don't need two loops. This is a single result set with three columns, as my rewrite clearly shows. You only need one loop to read all the results.
SELECT count(*) FROM subjects GROUP BY subjects.classid
You don't need the "subselect", you can just do a JOIN and count()
SELECT
class.*,
count(subjects.*) AS total_subjects
FROM
class
LEFT JOIN subjects ON class.classid = subjects.classid
WHERE
class.teacherid = ?
GROUP BY
class.classid

Categories