Best way to search Mysql - large where or multiple where statements - php

Im just looking for the best way to go about building this code for search query.
Say I have a search that has to search for 6 columns and an advance search that looks at 14 columns.
I was thinking of compressing it to search 1 column with a large number.
So for example say I have categories 'city, price, date, type, area' then about 8 'yes' or 'no' (or 1/0) columns. I am just wondering which search would work better/faster.
SELECT * FROM table WHERE city=$city AND price BETWEEN ($price) AND other_searchables = [3-9][2-9]__1_1_110110_____1_1
With this code the custom fields are put into one field and given 0=no or 1=yes and the beginning searches to see if the first 2 numbers equal 3+ and 2+ then _ for any characters. So for example other_searchables field would look like '2300111011011100000101'
OR use WHERE for each column?
SELECT * FROM table WHERE city=$city AND price BETWEEN ($price) AND type = $types AND area = $areas AND customfield = 1 AND customfield = 0 AND customfield = 1 AND customfield = 1 AND customfield = 0 AND customfield = 1 AND customfield = 0 AND customfield = 1
I am going to index city as this will be searched everytime... Just looking for any advice on which way to go here as far as which query would be better.
Thanks

Related

I want to subtract 2 columns of two different tables

tbl_blood:
id|qty
1 14
2 15
3 16
tbl_blood_list:
id|blood_quantity
1 1
2 1
3 1
My question is the tbl_blood table's column qty should subtract the blood_quantity column in tbl_blood_list's table. I need codes that I can implement in my php mysqli.
I have tried this code but it really cannot work:
$add_inv = "UPDATE tbl_blood
SET qty=(qty - '$blood_quantity')
WHERE id='$minus_blood_id' ";
If i m not misjudging your question so you want to update join but in your table structure i can't find any common column if there is Any common column in both table so you can join and update your table in one statement i think id are common in both table if yes so try this
$pdo->query("UPDATE tbl_blood AS tb JOIN tbl_blood_list AS tbl ON (tbl.id = tb.id) SET tb.qty = (tb.qty - tbl.blood_quantity)")
Please let me know if there is any problem or i misjudge your issue
Hope it's help you.
There are more efficient ways to handle this kind of situation, you should consider a different structure for your program/tables, why having two separate tables when tbl_blood could be decremented directly.
With your current structure, the code below works:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=blood', 'user', 'password');
/* Retrieve values to update */
$result = $pdo->query('
SELECT tb.id, (tb.qty - tbl.blood_quantity) n
FROM tbl_blood tb
LEFT JOIN tbl_blood_list tbl ON (tbl.id = tb.id)');
/* Update */
foreach ($result as $row)
$pdo->query('UPDATE tbl_blood SET qty = '.(int)$row['n'].' WHERE id = '.(int)$row['id'].' LIMIT 1');
?>
Result:
tbl_blood
id|qty
1 13
2 14
3 15
Also:
You may consider harmonizing your field names (e.g. 'blood_qty' and not 'blood_quantity')
Make sure "qty" and "blood_quantity" fields are unsigned integers
Make sure too that both "id" fields are primary keys
You may add a timestamp field in both tables to keep track of updates

MySQL/PHP - Need to be able to produce query results with certain columns having more weight than others

I have been tasked with creating a search function that when searched, certain fields will have more weight than others.
Here is an simplified example.
cars (table)
year, make, model, color, type (columns)
Let's say someone searches for the following:
Year: 1968
Make: Ford
Model: Mustang
Color: Red
Type: Sports Car
If the cars in the table have none of the correct fields they should not show up, but if record has some of the correct fields but not all they should still show up. But certain fields should be weighted higher than others.
For instance maybe they are weighted like this:
Column - Weight
Year - 30
Make - 100
Model - 85
Color - 10
Type - 50
So if a record matches the search in the "make" field and the "model" field, that record would be above a record that matched in the "year", "color" and "type" field, because of the weights we placed on each column.
So lets say that the query matches at least one field for two records in the database, they should be ordered by the most relevant based on the weight:
1971, Ford, Fairlane, Blue, Sports Car (weight = 185)
1968, Dodge, Charger, Red, Sports Car (weight = 90)
I have been racking my brain trying to figure out how to make this work. If anyone has done something like this please give me an idea of how to make it work.
I would like to do as much of the work in MySQL as possible via joins, I think this will be bring up the results faster than doing most of the work in PHP. But any solution to this problem would be much appreciated.
Thanks in advance
Bear with me, this is going to be a strange query, but it seems to work on my end.
SELECT SUM(
IF(year = "1968", 30, 0) +
IF(make = "Ford", 100, 0) +
IF(model = "Mustang", 85, 0) +
IF(color = "Red", 10, 0) +
IF(type = "Sports Car", 50, 0)
) AS `weight`, cars.* FROM cars
WHERE year = "1968"
OR make = "Ford"
OR model = "Mustang"
OR color = "Red"
OR type = "Sports Car"
GROUP BY cars.id
ORDER BY `weight` DESC;
Basically, this groups all results by their id (which is necessary for the SUM() function, does some calculations on the different fields and returns the weight as a total value, which is then sorted highest-lowest. Also, this will only return results where one of the columns matches a supplied value.
Since I don't have an exact copy of your database, run some tests with this on your end and let me know if there's anything that needs to be adjusted.
Expected Results:
+============================================================+
| weight | year | make | model | color | type |
|============================================================|
| 130 | 1968 | Ford | Fairlane | Blue | Roadster |
| 100 | 2014 | Ford | Taurus | Silver | Sedan |
| 60 | 2015 | Chevrolet | Corvette | Red | Sports Car |
+============================================================+
So, as you can see, the results would list the closest matches, which in this case are two Ford (+100) vehicles, one from 1968 (+30), and a Red Sports Car (10 + 50) as the closest matches (using your criteria)
One more thing, if you also want to display the rest of the results (ie results with a 0 weight match score) simply remove the WHERE ... OR ..., so it will check against all records. Cheers!
Further to the comments below, checking the weight after a LEFT JOIN on a pivot table:
SELECT SUM(
IF(cars.year = "1968", 30, 0) +
IF(cars.make = "Ford", 100, 0) +
IF(cars.model = "Mustang", 85, 0) +
IF(cars.color = "Red", 10, 0) +
IF(types.name = "Sports Car", 50, 0)
) AS `weight`, cars.*, types.* FROM cars
LEFT JOIN cars_types ON cars_types.car_id = cars.id
LEFT JOIN types ON cars_types.type_id = types.id
WHERE year = "1968"
OR cars.make = "Ford"
OR cars.model = "Mustang"
OR cars.color = "Red"
OR types.name = "Sports Car"
GROUP BY cars.id
ORDER BY `weight` DESC;
Here is a picture of the LEFT JOIN in practice:
As you can see, the Cobalt matches on color (silver) and model (Cobalt) (85 + 10) while the Caliber matches on type (Sports Car) (50). And yes, I know a Dodge Caliber isn't a Sports Car, this was for example's sake. Hope that helped!
If I understand your logic you can just do something like direct comparison in PHP between the value requested and the value returned.
The query will sound like:
SELECT Year,Make,Model,Color,Type
FROM table
WHERE year='$postedyear' OR make='$postedmake'
OR model='$postedmodel' OR color='$postedcolor'
Then in php looping between the results:
foreach($results as $result){
$score = 0;
if($result['year']==$postedyear{$score=$score+30;}
//continue with the other with the same logic.
}
After each foreach iteration $score will be the score of that selected row. If you push the score to the $result array you can also sort it by score before displaying the results.
Variation on #lelio-faieta
In php you can have a result array containing arrays of values for each item matching at least one of the search terms, the associative array of values to match and the associate array of weights, both with the same indexes. You would just get an array of matches for each index. (maybe use array_intersect_assoc()) Then you multiply by the weights and sum, add to the original data. Then you do have to sort the result array at that point.
There is a solution doing this via the mysql query directly, but that would end up with an overgrown resource thirsty query for every single search you perform.
Doing it in PHP is not much difference in resource usage, bounding to several loops in results and processing it.
I've had a very similar project and my best suggestion would be: "use SphinxSearch"
Very easy to install, needs a bit of a learning curve to setup afterwards, but very similar to mysql queries etc. With this you can apply weights to every column match and rank your results afterwards.
Also, it is a multitude of time faster that typical mysql queries.

count number of characters of a particular field in a databse

The below shows my table users and some entries in the table. In users table phoneid is stored in particular manner as shown below .Some values are above 16 and some others are below 16 .
I want to display phoneid into two categories (One is above 16 & another one is below 16).
How can I categorize it into two category ???
phoneid
43f46cc43f4c797c
03C03372-B549-4FA2-9A6B-E28F250E8DWE
4f7e35b38470abd0
74C03372-B549-4FA2-9A6B-E28F250E8EBA
bbc4fe41d79aa730
61152d549ba924fa
c3ac44f7c0cd2a62
03C03372-B549-4FA2-9A6B-E28F250E8DWE
d07e063ef6d4598e
2f3467d189e6a8ac
fc35ee8d18f03b9e
189cc387b2b3ce3f
84B03372-B549-4BN2-9A6B-E28F250E8OPJ
Thanks in advance
If you need just to list the users and order them according to their length
you can simply run
SELECT * FROM tableName ORDER BY length(phoneid);
but if you need to get for example the ones with length greater than 16 just do this :
SELECT * FROM tableName WHERE length(phoneid) > 16;
you should use.. strlen() function of php..
which will return the character count of a string..
like
echo strlen($phoneid);
The length function in MySQL can take care of that.
SELECT * FROM users WHERE LENGTH(phoneid) > 16;
I think so this is what you are looking for:
SELECT * FROM tableName GROUP BY length(phoneid);
I want to display phoneid into two categories (One is above 16 & another one is below 16). How can I categorize it into two category ???
You can add temp field to your result sets, as below, to show the phone ids as defined categories based on their char lengths.
Select *,
case when char_length( phoneid ) <= 16 then
phoneid else '' end as 'Phone_Category_1',
case when char_length( phoneid ) > 16 then
phoneid else '' end as 'Phone_Category_2'
From users
Sample Result would be: (rest of the columns not shown here):
phoneid Phone_Category_1 Phone_Category_2
43f46cc43f4c797c 43f46cc43f4c797c
03C03372-B549-4FA2-9A6B-E28F250E8DWE 03C03372-B549-4FA2-9A6B-E28F250E8DWE
4f7e35b38470abd0 4f7e35b38470abd0
74C03372-B549-4FA2-9A6B-E28F250E8EBA 74C03372-B549-4FA2-9A6B-E28F250E8EBA
You can omit phoneid from result set and can depend on phone_category_1 and phone_category_2.

Exploding data from mysql using PHP

I have a database table where I am storing all the values from an external xml file. Since my project requirement demands me to deal with this unnormalized data. I need to help to extract data in an appropriate way.
I have two web pages (one for categories) and one for products.My database table looks like this:
**product_id Code Name ProductRange ProductSubRange WebCategory**
1 1002 Lid 30l Crystal;Uni LIDs Household products
2 10433 Casa Basket Silver Casa Casa Hipster BASKET Kitchenware
3 17443 Casa Basket Ice White Casa;Laundry LAUNDRY BASKET Laundry products
4 13443 Garden tub Eden Eden Garden Pots Household products
5 199990 Black Lid 201 Crystal Crystal Lids Household products
The product that belong to more than one productRange is indicated my semicolon(;). For example,above product_id 1 with name "Lid 301" belongs to two Product Ranges "Crystal" and "Uni". Same is for product_id 3. However product 2 belongs to single ProductRange.
MY QUESTIONs:
1) How can I construct a query so that it could return "ProductRange" based on my query_string values of "Webcategory"? For example:
if I get "Household Products" as my WebCategory from query string, it could give me distinct like this:
Household Products
|-Crystal
|-Uni
|-Eden
Laundry Products
|-Casa
|-Laundry
Kitchenware
|-Casa
2) Based on extracted ProductRanges, I want to display products separately in my webpages according to the product range and webcategory. For example, if I choose "Crystal" from above, it could give me Products with product_id "1" and "5" respectively like this:
Household Products|
|-Crystal
|-Lid 301 (product_id=1)
|-Balck Lid 201 (product_id=5)
|-Uni
|-Lid 301 (product_id=1)
|-Eden
|-Garden Tub
Kitchenware|
|-Casa
|-Casa Basket silver
Laundry Products|
|-Casa
|-Casa Basket Ice White
|
|-Laundry
|-Casa Basket Ice White
Can anyone guide me how can I retrieve records from the database and what I will need to do as I am new to programming? I would appreciate if anyone could help me in this regard.
In order to get distinct product ranges based on a give WebCategory input = 'XYZ', you can use the following - don't be intimidated by the numberstable, it's just a helpful table that contains rows each with increasing integer values from 1 ... up to N where N is the maximum number of characters in your ProductRange column. These can be made by hand or using a special insert/select query like the one found here:
http://www.experts-exchange.com/Database/MySQL/A_3573-A-MySQL-Tidbit-Quick-Numbers-Table-Generation.html
SELECT DISTINCT
SUBSTRING(ProductRange FROM number FOR LOCATE(';', ProductRange, number) - number) AS ProductRange
FROM (
SELECT ProductRange, CASE number WHEN 1 THEN 1 ELSE number + 1 END number
FROM (
SELECT mydatabasetable.ProductRange, numberstable.number
FROM mydatabasetable
INNER JOIN numberstable
ON numberstable.number >= 1
AND numberstable.number <= CHAR_LENGTH(mydatabasetable.ProductRange)
WHERE WebCategory = 'XYZ'
) TT
WHERE number = 1 OR (number + 1) <= CHAR_LENGTH(ProductRange)
) TT
WHERE SUBSTRING(ProductRange FROM number FOR 1) = ';'
OR numberstable.number = 1;
In order to retrieve a result set with all values WebCategory, ProductRange and Product for your website you can use the below slightly modified version derived from the above query. So that the results will appear more meaningful at first, I added an ORDER BY clause to keep all same-category, same-product-range products in sequence one after the other. This might or might not be desired as you might prefer to do that in your application/server-script code. In that case you can remove the ORDER BY clause without doing any harm.
SELECT WebCategory,
SUBSTRING(
ProductRange
FROM number
FOR LOCATE(';', ProductRange, number) - number
) AS ProductRange,
Product
FROM (
SELECT WebCategory, ProductRange, Product,
CASE number
WHEN 1 THEN 1
ELSE number + 1
END number
FROM (
SELECT WebCategory, ProductRange, Product, numberstable.number
FROM mydatabasetable
INNER JOIN numberstable
ON numberstable.number >= 1
AND numberstable.number <= CHAR_LENGTH(ProductRange)
) TT
WHERE number = 1 OR (number + 1) <= CHAR_LENGTH(ProductRange)
) TT
WHERE SUBSTRING(ProductRange FROM number FOR 1) = ';'
OR numberstable.number = 1
ORDER BY WebCategory, ProductRange, Product
You are probably going to want to do a GROUP BY clause in your query and maybe an JOIN if the detailed data is in a different table. If I understand you correctly it would look something like this.
SELECT T.WebCategory, T.ProductRange, T2.Product FROM table T
INNER JOIN table2 T2 ON T2.ProductRange = T.ProductRange
WHERE T.WebCategory = 'Household products'
GROUP BY T.WebCategory, T.ProductRange, T2.Product
It is tough to test my query without having a database setup to test against, but something like the above should return what you are looking for. You will of course need to rename your columns based on the actual names in the second table. Overall though, this should get you started if I understood the question correctly.

MYSQL sort function, depending on the arithmetic operation using the database field values as array keys

I have table of products, and there are 2 fields: price, currency. Possible values in currency fields are 1,2,3. 1 for MDL, 2 for USD, and 3 for EUR. I have to sort my products by price in mdl currency. So i have an array with rates:
$v = array(1,11.38,15.8);
Help my please with my query, I've tried something like this, but I've got errors:
$results = $this->Product
->query("SELECT `id`,`price`,`currency` FROM products
ORDER BY price*$v[currency] DESC");
Hmm... I`ll try to explain, through an example.
My table:
id|price|currency
_________________
1 | 500 | 2
2 | 300 | 3
It shows that first products price is saved in USD, and second products price is saved in EUR. But i have to sort them in MDL valute. So i get an array of rates for each value:
$rates = array([1] = > 1, [2] => 11.50, [3] => 15.50);
So i have to order my products by the result of formula: price*value rate
in first case: 500*$rates['currency value from db, in our case 2] = 500 * 11.50 etc.
Thanks in advance.
Because of the extended example on this problem I have edited this query.
Lets assume that the currencies are alse stored in some table, lets say currency (if not, it should be anyway).
Table currency should be as follows:
ID VALUE CODE
-----------------------------
1 1 USD
2 11.38 EUR
3 15.8 MDL
Then the query should be:
SELECT p.`id`, p.`price`, p.`price` * c.`value` AS 'ratio'
FROM products p
LEFT JOIN currency c ON c.`id` = p.`currency`
ORDER BY `ratio` DESC
By this query You select the currency value from the table currency depending on the currency ID from products table and finaly the results are ordered by the ration price * currency value.
I understand that maybe You have the currencies only hardcoded as array within some config, but it really would be better to put the currencies into the DB (if it is not).
You can`t use data base column name as array keys, because mysql is later instance than php. In php you simply generate query string that is passed to database managment system.
Your query should look like this:
$results = $this->Product->query
(
"SELECT `id`,`price`,
CASE `currency`
WHEN '1' THEN $v[0]
WHEN '2' THEN $v[1]
WHEN '3' THEN $v[2]
END AS 'ratio'
FROM products
ORDER BY price*ratio DESC
"
);

Categories