multiple criteria search - php

I have multiple tables with Customer data (ex Customer Name, Customer Contact Name, Customer Service Item etc).
I need to enable search on these multiple columns across tables. Then I have to use the search result to pull Customer information (I need Customer ID, or Customer Name).
What is the best way to do this?
Possible Solutions:
Offer multiple filters (different search boxes), and then handle each result separately. (The client does not prefer this, and wants it in a single box.)
Create a temp table (CustomerID, Search Field Values).
Create index !?
User inner join, and put logic into handling the search result!!!!
Thanks.

try something like:
SELECT
c.*
FROM CustomerTable c
INNER JOIN (SELECT
CustomerID
FROM Table1
WHERE columnA =filter1
UNION
SELECT
CustomerID
FROM Table2
WHERE columnB =filter2
UNION
SELECT
CustomerID
FROM Table3
WHERE columnC =filter3
) dt ON c.CustomerID=dt.CustomerID

http://dev.mysql.com/doc/refman/5.1/en/fulltext-search.html
I do believe this similar to Oracle Text Search etc which is used in Oracle applications to allow more intelligent searches, "google-likish".
So it is a fulltext index which is to be created.
Doing it with inner joins (or worse, copying stuff around in temporary tables) might work but the code will be complex and you might kill performance.

the only thing you can do if he customer insists on making a search that works like this is to create a TEXT column, FULLTEXT index it, and concatenate all of the columns you want to search in to this column. if you do this, i suggest that you write your queries in this form to guarantee correct matches while maintining a sort orderthat makes sense:
select *
from sometable
where match(search_column) against('$search_terms' in boolean mode)
order
by match(search_column) against('$search_terms')

Related

Postgres query SELECT when field is set

I have a table topics. And I have two queries which select different data, one for the table votings and one from the table messages. topics has a field m_group which is a foreign key for messages. This field can either be NULL or a group for entries in messages (group is a field in this table). If this field is set to a group, it should perform query 1 to select all messages or if it is not set it should perform query 2 to select all votings for this specific topic. I'm using Postgres and PHP on an Apache Webserver.
Now my question is what is the recommended way to go. I came up with two solutions (not sure if solution 2 is actually possible, haven't tried it yet).
Solution 1
First select the field m_group. Then determine if it is set via PHP and perform the associated query.
Solution 2
Use a IF THEN ELSE statement
Basically the query should then look something like this
IF t.m_group IS NULL
THEN
query2
ELSE
query1
As already mentioned, I'm not sure if solution 2 is possible. What would be the best way to handle this? Solution 1 performs two queries, I think this is inefficient.
UPDATE
As mentioned above, it should perform the queries for a specific topic. You have the id of this topic. How can you specify this in solution 2? And does the IF THEN ELSE statement already know the alias t for topics, if that is specified in the queries?
If I understand you right, you need to use two subqueries as sets of data, while you should use one of them as a source depending on what's in the m_group field. Your second approach is good if you only select a few rows. However, if you need to grab a lot of data from the table, this way you will need to perform too many subqueries. I would rather first grab all the data you need from topics and then select what you need with both queries.
Pseudo SQL query will look like that:
with t1 as (select t.id, group_m, ... from topics t where t.m_group is not null),
t2 as (select t.id, ... from topics t where t.m_group is null)
select id, title, SUM(subtable_id) AS votes
from query1
join t1 on t1.id = query1.id and...
union
select id, title, SUM(subtable_id) AS votes
from query2
join t2 on t2.id = query2.id and...

Add Column to one Table that is COUNT of another

I have a list of subscribers in table Subscribers. Every time they receive a copy of their subscription, a new record is created in Subscriptions_Fulfilments for each Subscribers.ID.
I can create a table showing each Subscriber ID and the number of copies they received with the following query:
SELECT Sub_ID, COUNT(Sub_ID) fcount FROM `Subscriptions_Fulfilments`
GROUP BY Sub_ID
But I need to create a compound query that returns Subscribers along with a column showing the COUNT(Sub_ID) of Subscriptions_Fulfilments.
So I have two questions:
A) How would you make a query to create a table that shows each Subscriber and the number of times they've received their subscription, based on the COUNT of that Subscriber's ID in Subscriptions_Fulfilments?
B) I'm operating under the assumption that a single MySql query accomplishing this would be more efficient than, say, running two queries, the one above and a SELECT * FROM Subscriptions, and combining the resulting arrays in PHP. I have a feeling I know the answer but I'd like to positively learn something new today.
Unfortunately, after too many tries, I'm clearly not good enough at queries for this and I have very little past the above query to show for it. I apologize if this ends up being a dup, I searched long and hard before asking, but it's quite difficult to search precisely for Query help...
Here is a simple example showing the Subscribers ID and the no of subscription they have received. Hope it helps.
Step 1: select the ids from the Subscriber table
Step 2: select the no of counts of subscriptions received by each subscriber.
Step 3: Join both the table ON the basis of ID.
SELECT SubId, noSub FROM
Subscribers sb JOIN (SELECT SubId AS sid, COUNT(*)AS noSub FROM Subscriptions_Fulfilments GROUP BY SubId)AS ss ON sb.SubId = ss.sid
One of the big advantages of a relational database is the ability to do joins and combinations of the data in your tables in a way that allows for this functionality without having to actually store it in a separate table.
You can accomplish this with a subquery like this:
SELECT Subscribers.name, fulfilments.count FROM Subscribers
INNER JOIN (
SELECT id, count(*) as count FROM Subscriptions_Fulfilments
GROUP BY Sub_Id
)fulfilments ON subscribers.id = fulfilments.id
This might not be 100% what you're looking for and I might have messed up your names, but hopefully this will start to get you in the neighborhood of being correct?
Simply try execute this query:
Select distinct Sub_ID, count from (SELECT Sub_ID, COUNT(Sub_ID) fcount FROM Subscriptions_Fulfilments
GROUP BY Sub_ID);

Can I have a loop within an SQL SELECT statement?

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

MYSQL Query, Optimize LIKE and NOT IN

I have a query I need to run which is currently running extremely slow. The table I am working with has nearly 1 million records.
What I need to do is search for items with a LIKE name : Example "SELECT name, other, stuff FROM my_table WHERE name LIKE '%some_name%'"
In addition it has to be able to exclude certain terms with wildcards from the search results so it turns into something like this :
SELECT name, other, stuff
FROM my_table
WHERE name LIKE '%some_name%'
AND name NOT IN (SELECT name FROM my_table WHERE name LIKE '%term1%' AND name LIKE '%term2%' AND name LIKE '%term3%')
To top things off, I have two INNER JOINs and I have to execute it in PHP. I have indexes on the table for relevant columns. It is on a server that is fast for pretty much every other query I can throw at it.
The question is, is there a different method I could be using to do this type of query thats is quick?
UPDATES
This is my actual query after adding in FULLTEXT index to the chem table and trying the match function instead. NOTE : I don't have control over table/column names. I know they dont look nice.
CREATE FULLTEXT INDEX name_index ON chems (name)
SELECT f.name fname, f.state, f.city, f.id fid,
cl.alt_facilid_ida, cl.alt_facili_idb,
c.ave, c.name, c.id chem_id,
cc.first_name, cc.last_name, cc.id cid, cc.phone_work
FROM facilities f
INNER JOIN facilitlt_chemicals_c cl ON (f.id = cl.alt_facilid485ilities_ida)
INNER JOIN chems c ON (cl.alt_facili3998emicals_idb = c.id)
LEFT OUTER JOIN facilities_contacts_c con ON (f.id = con.alt_facili_ida)
LEFT OUTER JOIN contacts cc ON (con.alt_facili_idb = cc.id)
WHERE match(c.name) against ('+lead -gas' IN BOOLEAN MODE)
That is just an example of a simple query. The actual terms could be numerous.
You want to implement MySQL's full text capabilities on the columns you're searching. Read more about it here.
Moreover, you probably want to do a boolean search:
select
t1.name,
t1.stuff
from
my_table t1
where
match(t1.name) against ('+some_name -term1 -term2 -term3' in boolean mode)
I would suggest to use external Full-text engine like Lucene or Sphinx. Especially if you plan to fire search queries against relatively big datasets.
In Sphinx case you can use SELECT-like syntax called SphinxQL which will do exactly as required:
SELECT * FROM <sphinx_index_name> WHERE MATCH('some_name -term1 -term2 -term3');
as described in http://sphinxsearch.com/docs/current.html#extended-syntax (NOT operator in your case). Plus you could enable morphology support which might be helpful as well.
You can combine MySQL and Sphinx queries with http://sphinxsearch.com/docs/current.html#sphinxse

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.

Categories