Not sure if it's possible to do this in SQL but... I'm having difficulty selecting results between certain criteria. I have a column that is a mixture of text and numeric. For example: LOC:05-04-01. I'm wanting to select items between two locations. EG: between LOC:05-04-01 and LOC:05-04-20.
I've tried using the standard BETWEEN statement but it returns an empty result.
$loc1 = 'LOC:05-04-01';
$loc2 = 'LOC:05-04-20';
$sql = $dbh->prepare("SELECT * FROM table WHERE location BETWEEN ? AND ? ORDER BY location DESC");
$sql->execute([$loc1,$loc2]);
while ($row = $sql->fetch(PDO::FETCH_ASSOC)) {
echo $row['ID'].': '.$row['location'].'<br>';
}
My database is similar to below:
ID | Location
1 | LOC:05-04-01
2 | LOC:05-04-02
3 | LOC:05-04-05
4 | LOC:06-04-01
5 | LOC:06-04-02
6 | LOC:06-04-10
I'm expecting to see a list of locations out of the above query such as:
1: LOC:05-04-01
2: LOC:05-04-02
3: LOC:05-04-05
This code should do what you want:
SELECT *
FROM <table>
WHERE location BETWEEN 'LOC:05-04-01' AND 'LOC:05-04-20'
ORDER BY location DESC;
You are doing string comparisons, and the values compare as strings.
You should test this using a direct query on the database. If this doesn't work, then you might have data in columns that you don't expect -- say the hyphens are really a different character.
If the PHP code does not work, something is going wrong at that level. You might have an error in your query (say connected to the wrong database). You might have bad characters in your constants.
Related
I need to get a SUM off all numerical entries in one of the tables of my DB
id | parameter
--------------
1 | 5
2 | 1
3 | 11
4 | 3
My php is:
$total = 'SELECT parameter FROM resource_table';
$res = $db->prepare($total);
$res->execute();
while($row4 = $res->fetch()) {
$count_sum1[$row4['parameter']][] = $row4;
}
$count_sum = array_sum( $count_sum1 );
print<<<END
$count_sum
END;
this is not working, as I can guess I am not doing something correctly.
Please help
Thanks for your help in advance
Let the database do the work:
SELECT SUM(parameter) FROM resource_table
In case you want to stick to php:
<?php
// code
while($row=$res->fetch())
$count_sum+=row["parameter"];
// code
?>
Yes, database engine supports simple operations, such as SUM(), AVG(), MIN() and lot of others... so actually you are able to do some basic operation on the specific engine. Read a documentation to your database engine, because you could use MySql, MSSQL, or plenty of others and every use its own type of functions.
But I suppose that you use MySQL, so the function is simply SUM():
SELECT SUM(parameter) FROM tableName;
My table looks something like this:
id | title | tags
1 | I like things | 9,3,7,10
2 | Stuff about i | 12,3,7,10
3 | Overly loaded | 1,3,4,5
4 | Never ever AT | 12,10
4 | Forever alone | 9
Note: the names were made up, they're only for illustration purposes
Things about the tags:
The numbers represent the numeric ID of a given tag, ranging from 0 to 12
1, 9 and 12 will always be the first tag in this list, followed by the rest of the tags in a sorted order from low to high
An entry can also have only one tag, 1, 9 or 12.
I want to add a filter feature, which allows the user to filter the list using these tags. Let's ignore the client-side part, and only care about what we recieve.
Let's say, the script receives the following input (which is always sorted): "3,4,9"
Then, I would like to pass this to a MySQL query in order to retrieve the matching items. The site also has a pagination feature, but I'm only saying this because it may affect the order of the statements, it's not important otherwise. It just adds a LIMIT statement at the end.
Putting all escaping and what not aside, the query I tried to use was
SELECT * FROM `$tablename` WHERE tags LIKE '%$input%' ORDER BY title ASC LIMIT 0,10
But this - of course - didn't work. I gave me this nice little error message, saying:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''%3,4,9%' ORDER BY title ASC LIMIT 0,10' at line 1 in script.php on line n
I would like to know how to alter this query in order to be able to properly filter the results, even if it takes an extra function to append multiple LIKE statements together.
WHERE tags LIKE '%$input%'
Will only work when the list of input tags is found within the tags string. "3,4,9" is not like "3,4,5,9"
You have a denormalized structure, which is causing you problems, one row per tag value is what you should implement.
The alternative is to break apart the input string and use multiple OR LIKE statements:
WHERE tags LIKE '%input1%'
OR tags LIKE '%input2%'
....
Then you have to deal with the issue of 1 matching to 11, which means concatenating commas on the front and back of each side of the LIKE statement, or padding with some value. The workaround becomes an ugly beast that's far less efficient than fixing the underlying design problem.
I know this won't be the accepted answer, but because there may be cases where changing the database structure may not be possible, I thought I'd put this out there as a precise answer to the question.
Given the constraints you've stated, and assuming PHP is your control language (since the question is tagged with PHP), I would build the SQL statement using PHP:
<?php
// assuming $tags contains your comma-delimited tags
$tags_array = explode(',',$tags);
$where = 'where true';
for($i=0;$i<sizeof($tags_array);$i++) {
switch($tags_array[$i]) {
case '1':
$where .= " and tags LIKE '1%' and tags NOT LIKE '12%'";
break;
case '9':
case '12':
$where .= " and tags LIKE '" . $tags_array[$i] . "%'";
break;
default:
$where .= " and tags LIKE '%," . $tags_array[$i] . "%'";
break;
}
}
$sql = "SELECT * FROM `$tablename` " . $where . " ORDER BY title ASC LIMIT 0,10";
?>
Having said that, I agree with everyone commenting above: it's much better to get the data model right in the database.
I have a sql query that, for each result returns a string like 1:One, 2:Two, 3:Three.
Now I want to convert each one of these strings to a PHP array like this:
Array(
1: One,
2: Two,
3: Three
)
I know that I could do that with one explode function inside another one but, isn't that too much overkill if I have 500+ results on the mysql query? Is there any better way to get something like that?
Here is a sample of the mysql code that creates something like the string result that I gave:
GROUP_CONCAT(DISTINCT cast(concat(cast(number.id AS char),': ',number.name) AS char) order by number.id SEPARATOR ', ') AS all_active_numbers
EDIT
So here's an example of 2 possible returning rows from mysql:
|-----------------------------------------------------------------------|
| id | all_groups | groups_assigned |
| 1 | 1:Team A, 2:Team B, 3:Team C | 1:Team A |
| 2 | 1:Team A, 2:Team B, 3:Team C | 2:Team B, 3:Team C |
|-----------------------------------------------------------------------|
What I want to know is the best way to transform the strings of all_groups and groups_assigned of each row, into a PHP array. As I said, I know I could do it using 2 explode function (one inside another using foreach loops) but what if my query returns 500+ results? This seems like a big overkill for the server to compute explode's for each one of the 500+ rows.
Just to clarify, all_groups is something like the groups that are available for a person and groups_assigned is the groups where the person is registered from the available all_groups.
Another possibility is maybe divide this into 3 different queries?
Just explode based off of your colon, otherwise, form your query to provide the KEY and VALUE's separately.
PHP example (untested, example only):
$result = $pdo->query($query);
$myArray = array();
while($row = $result->fetchAll(PDO::FETCH_ASSOC)) {
$myGroup = explode(": ", $row['all_active_numbers']);
$myArray[][$myGroup[0]] = $myGroup[1];
}
var_dump($myArray);
I'm trying to create a search engine for an inventory based site. The issue is that I have information inside bbtags (like in [b]test[/b] sentence, the test should be valued at 3, whereas sentence should be valued at 1).
Here is an example of an index:
My test sentence, my my (has a SKU of TST-DFS)
The Database:
|Product| word |relevancy|
| 1 | my | 3 |
| 1 | test | 1 |
| 1 |sentence| 1 |
| 1 | TST-DFS| 10 |
But how would I match TST-DFS if the user typed in TST DFS? I would like that SKU to have a relevancy of say 8, instead of the full 10..
I have heard that the FULL TEXT search feature in MySQL would help, but I can't seem to find a good way to do it. I would like to avoid things like UNIONS, and to keep the query as optimized as possible.
Any help with coming up with a good system for this would be great.
Thanks,
Max
But how would I match TST-DFS if the user typed in TST DFS?
I would like that SKU to have a relevancy of say 8, instead of the full 10..
If I got the question right, the answer is actually easy.
Well, if you forge your query a little before sending it to mysql.
Ok, let's say we have $query and it contains TST-DFS.
Are we gonna focus on word spans?
I suppose we should, as most search engines do, so:
$ok=preg_match_all('#\w+#',$query,$m);
Now if that pattern matched... $m[0] contains the list of words in $query.
This can be fine-tuned to your SKU, but matching against full words in a AND fashion is pretty much what the user presumes is happening. (as it happens over google and yahoo)
Then we need to cook a $expr expression that will be injected into our final query.
if(!$ok) { // the search string is non-alphanumeric
$expr="false";
} else { // the search contains words that are no in $m[0]
$expr='';
foreach($m[0] as $word) {
if($expr)
$expr.=" AND "; // put an AND inbetween "LIKE" subexpressions
$s_word=addslashes($word); // I put a s_ to remind me the variable
// is safe to include in a SQL statement, that's me
$expr.="word LIKE '%$s_word%'";
}
}
Now $expr should look like "words LIKE '%TST%' AND words LIKE '%DFS%'"
With that value, we can build the final query:
$s_expr="($expr)";
$s_query=addslashes($query);
$s_fullquery=
"SELECT (Product,word,if((word LIKE '$s_query'),relevancy,relevancy-2) as relevancy) ".
"FROM some_index ".
"WHERE word LIKE '$s_query' OR $s_expr";
Which shall read, for "TST-DFS":
SELECT (Product,word,if((word LIKE 'TST-DFS'),relevancy,relevancy-2) as relevancy)
FROM some_index
WHERE word LIKE 'TST-DFS' OR (word LIKE '%TST%' AND word LIKE '%DFS%')
As you can see, in the first SELECT line, if the match is partial, mysql will return relevancy-2
In the third one, the WHERE clause, if the full match fails, $s_expr, the partial match query we cooked in advance, is tried instead.
I like to lower case everything and strip out special characters (like in a phone number or credit card I take everything out on both sides that isn't a number)
Rather than try to create your own FTS solution, you could try to fit the MySQL FTS engine to your requirements. What I've seen done is create a new table to store your FTS data. Create a column for each different piece of data that you want to have a different relevance. For your sku field you could store the raw sku, with spaces, underscores, hyphens and any other special character intact. Then store a stripped down version with all these things removed. You may also want to store a version with leading zeros removed, as people often leave things like that out. You can store all these variations in the same column. Store your product name in another column, and the product description in another column. Create a separate index on each column. Then when you do your search, you can search each column individually, and multiply the rank of the results based on how important you think that column is. So you could multiply sku results by 10, title by 5 and leave description results as is. You may have to do a little experimentation to get the results you want, but it may ultimately be simpler than creating your own index.
Create a keywords table. Something along the lines of:
integer keywordId (autoincrement) | varchar keyword | int pointValue
Assign all possible keywords, skus, etc, into this table. Create another table, a post-keywords bridge, (assuming postId is the id you've assigned in your original table) along the lines of:
integer keywordId | integer postId
Once you have this, you can easily add keywords to each post as it is interested. To calculate total point value for a given post, a query such as the following should do the trick:
SELECT sum(pointValue) FROM keywordPostsBridge kpb
JOIN keywords k ON k.keywordId = kpb.keywordId
WHERE kpb.postId = YOUR_INTENDED_POST
I think the solution is quite straightforward unless I missed something.
Basically run two search, one is exact match, the other is like match or regex match.
Join two resultsets together, like match left join exact match. Then for example:
final_relevancy = (IFNULL(like_relevancy, 0) + IFNULL(exact_relevancy, 0) * 3) / 4
I didn't try this myself though. Just an idea.
I would add a column that is stripped of all special character's, misspellings, and then upcased (or create a function that compares on text that has been stripped and upcased). That way your relevancy will be consistent.
/*
q and q1 - you table
this query takes too much resources,
make from it update-query ( scheduled task or call it on_save if you develop new system )
*/
SELECT
CASE
WHEN word NOT REGEXP "^[a-zA-Z]+$"
/*many replace with junk characters
or create custom function
or if you have full db access install his https://launchpad.net/mysql-udf-regexp
*/
THEN REPLACE(REPLACE( word, '-', ' ' ), '#', ' ')
ELSE word
END word ,
CASE
WHEN word NOT REGEXP "^[a-zA-Z]+$"
THEN 8
ELSE relevancy
END relevancy
FROM ( SELECT 'my' word,
3 relevancy
UNION
SELECT 'test' word,
1 relevancy
UNION
SELECT 'sentence' word,
1 relevancy
UNION
SELECT 'TST-DFS' word,
10 relevancy
)
q
UNION
SELECT *
FROM ( SELECT 'my' word,
3 relevancy
UNION
SELECT 'test' word,
1 relevancy
UNION
SELECT 'sentence' word,
1 relevancy
UNION
SELECT 'TST-DFS' word,
10 relevancy
)
q1
it is a page coading where query result shows
**i can not use functions by use them work are more easier**
<html>
<head>
</head>
<body>
<?php
//author S_A_KHAN
//date 10/02/2013
$dbcoonect=mysql_connect("127.0.0.1","root");
if (!$dbcoonect)
{
die ('unable to connect'.mysqli_error());
}
else
{
echo "connection successfully <br>";
}
$data_base=mysql_select_db("connect",$dbcoonect);
if ($data_base==FALSE){
die ('unable to connect'.mysqli_error($dbcoonect));
}
else
{
echo "connection successfully done<br>";
***$SQLString = "select * from user where id= " . $_GET["search"] . "";
$QueryResult=mysql_query($SQLString,$dbcoonect);***
echo "<table width='100%' border='1'>\n";
echo "<tr><th bgcolor=gray>Id</th><th bgcolor=gray>Name</th></tr>\n";
while (($Row = mysql_fetch_row($QueryResult)) !== FALSE) {
echo "<tr><td bgcolor=tan>{$Row[0]}</td>";
echo "<td bgcolor=tan>{$Row[1]}</td></tr>";
}
}
?>
</body>
</html>
I have a php file that search a SQL database. It takes a string from a textbox and tries to match it to various attributes for the database. Here is the code that performs the searched:
if ($filter['meta_info']) {
$search_string = $filter['meta_info'];
unset($filter['meta_info']);
$m_intSortField = null;
$m_strWhere .= (($m_strWhere) ? " AND " : "")."(MATCH (`courses`.`assigned_id`,`courses`.`title`,`courses`.`author`,`courses`.`keywords`,`courses`.` abstract`,`courses`.`objective`,`courses`.`summary`,`courses`.`copyright`,`courses`.`notes`) AGAINST ('".mysql_escape_string($search_string)."' IN BOOLEAN MODE))";
}
My problem is, I want it to return courses that have a partial match to the assigned ID not just a complete match. Anyone know how I could do this?
Turn off strict mode on your mysql options, or use LIKE.
SELECT id,name from LESSONS where name LIKE "English%";
returns
| id | Name
| 2 | English Literature
| 8 | English Language