PHP remove specific numbers from MySQL field - php

My db is structured like:
id | posts | groups
----+--------+-----------
1 | 10 | 2
2 | 30 | 2
3 | 20 | 2
4 | 50 | 2,8
5 | 54 | 2,8
When a user gets to 50 or more posts I want the script to remove the group '2'. There is already a prior script that adds the '8'.
I have this:
$cusstring = mysql_query("SELECT `groups` FROM `users` WHERE `postnum` >= 50 ");
$row = mysql_fetch_array($cusstring);
$array = explode(',', $row[groups]);
$pos = array_search('2', $array);
unset($array[$pos]);
$row[groups] = implode(',', $array);
mysql_query("UPDATE `users` SET `groups` = $row[groups] WHERE `postnum` >= 50 ");
It just doesn't seem to update though. I don't know if this is because it picks up multiple fields in the array or if I'm doing something wrong with the greater than or equal to symbol.
Can anyone offer a solution?
Thanks.
EDIT:
I've worked out that if I change the symbol to equal to the query works on the first row it comes across with a post count of 50 but it leaves the rest. It would appear it's only able to process one row.

I think this would work for you. I think a SQL approach would be more efficient but you've said you want to keep it in PHP.
$cusstring = mysql_query("SELECT `groups`, `id ` FROM `users` WHERE `postnum` >= 50 ");
while($row = mysql_fetch_array($cusstring)) {
$groups = mysql_real_escape_string(preg_replace('~(^|\s+)2(,|$)~', '', $row['groups']));
//regex demo https://regex101.com/r/eX7qD1/1
$id = (int)$row['id'];
mysql_query("UPDATE `users` SET `groups` = '$groups' WHERE `id ` = $id ");
}
Your code is only getting one record because you aren't looping the fetch.
Also don't put data that comes from your DB back into a query directly this can lead to a SQL injection. Note I cast the ID here to an int and escaped the groups value. This should prevent the possibility of an injection.
You should switch drivers to PDO or MYSQLI. Once using one of those drivers you can use prepared statements.
This solution also will put an empty value in the groups field if 2 was the only value.

Related

MYSQL: List all column names where their values = "Yes"

I have a table like so:
User_Id Column1 Column2 Column3
1 Yes No Yes
2
I want to use mysql query to list all the column names (there are more than 3) which match the User_Id '1' and have a value of 'Yes'.
I get an error:
Trying to get property 'num_rows' of non-object
Here is what I have tried:
<?php $myStats = $mysqli->query("SELECT COLUMN_NAME FROM user_services.columns WHERE myColumn = 'Yes'");
if ($myStats->num_rows > 0) {
// output data of each row
while($row = $myStats->fetch_assoc()) {
$rows[] = $row; }
return $rows; ?>
Please can someone show me where I am going wrong?
Thanks in advance.
The CONCAT_WS function comes in handy here:
SELECT CONCAT_WS(',', IF(Column1='Yes', 'Column1', NULL),
IF(Column2='Yes', 'Column2', NULL),
IF(Column3='Yes', 'Column3', NULL)) AS columns
FROM user_services.columns
WHERE User_Id = 1;
If you have more than 3 columns, then you may add more terms to above CONCAT_WS call. Your problem mainly seems to be a SQL one, so I won't add any PHP code.
Note that your design might be better off if your column strings were spread across rows, rather than columns. For instance, consider the following alternative:
User_Id | number | val
1 | 1 | Yes
1 | 2 | No
1 | 3 | Yes
Then, if you wanted all column numbers which were yes for user 1, you could simply do:
SELECT
User_Id,
GROUP_CONCAT(number ORDER BY number) columns
FROM yourTable
WHERE
User_Id = 1
GROUP BY
User_Id;

Get value from table with 3 unique ID's (redundant sql query)

$query = "SELECT COUNT(id) FROM complaint WHERE ID_complntCategory = ?";
$complntCategory = $database->prepare($query);
try {
$complntCategory->execute(array());
$complntCategory->setFetchMode(PDO::FETCH_ASSOC);
foreach ($complntCategory as $key) {
$totaalM = $key['1'];
$totaalV = $key['2'];
$totaalG = $key['3'];
}
}
catch(PDOException $e) {
echo "Error";
}
Above you see my PHP code, and here is what I'm trying to do:
I'm trying to get the amount of rows from the table 'complaint' into 3 different variables (totaalM, totaalV and totaalG). The totaalM variable should contain the amount of rows 'WHERE ID_complntCategory = 1'.
For the other variables the 'ID_complntCategory' should be 2 and 3
('ID_complntCategory' is either 1, 2 or 3)
There should be a way where I don't have to write 3 queries, right?
I'm clearly approaching this the wrong way, and I'm not sure how I should tackle this problem...
What you are trying to do is called pivot rows into columns, but MySQL doesn't have pivot table operator like other RDBMS, but you cane use the case expression like this in one query:
SELECT
SUM(CASE WHEN ID_complntCategory = 1 THEN 1 ELSE 0 END) AS totaalM,
SUM(CASE WHEN ID_complntCategory = 2 THEN 1 ELSE 0 END) AS totaalV,
SUM(CASE WHEN ID_complntCategory = 3 THEN 1 ELSE 0 END) AS totaalG,
COUNT(Id) AS Total
FROM complaint;
Or you can make it shorter like this:
SELECT
SUM(ID_complntCategory = 1) AS totaalM,
SUM(ID_complntCategory = 2) AS totaalV,
SUM(ID_complntCategory = 3) AS totaalG,
COUNT(Id) AS Total
FROM complaint;
Demo
This will give you something like this:
| totaalM | totaalV | totaalG | Total |
|---------|---------|---------|-------|
| 2 | 3 | 1 | 7 |
Here you need some magic, involving special SQL and PDO features.
First, you need an SQL query that is giving you desired results in one query. To get that you need a GROUP BY operator:
SELECT ID_complntCategory, count(*) FROM complaint GROUP BY ID_complntCategory
it will give you counts split by ID_complntCategory.
Next, you can use one of PDO's magnificent features, PDO::FETCH_KEY_PAIR fetch mode, that will give you an array where key would be category id and value is count
$sql = "SELECT ID_complntCategory, count(*) FROM complaint GROUP BY ID_complntCategory";
$stmt = $database->prepare($sql);
$key = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
$totaalM = $key['1'];
$totaalV = $key['2'];
$totaalG = $key['3'];
note that you should never catch a PDO errors only to say "error". Let PHP error reporting to do it instead.

update whole column with an array input from top to bottom without id info using prepared statements

I have 2 tables for my articles in DB:
articles
tags
tags table structure and current state:
primary key for my tags table is tag column.
tag | post_ids | count |
-------------------------------------
a | 2,0 | |
b | 1,2,0,0 | |
tag1 | 1,2,0,0 | |
j | 2,0 | |
I want to update count column after any INSERT (new post) or UPDATE (existing post) action and I want to do it with prepared statements.
For the time being, I can dynamically construct the input: $array = array("2","4","4","2") which is the related values of the count column to be updated.
And with the code below; I tried to update the count column however count always updates with value = 2 for all count rows.
I have no warning/error.
MySQL version: 5.5
I have to update 1st count row with the $array[0], 2nd count row with the $array[1] ... So I have to utilize LIMIT, OFFSET arguments
I learnt that MySQL doesn't allow OFFSET in UPDATE queries
I am not capable to write complex queries, I searched SO and found the Q&A: LIMIT offset or OFFSET in an UPDATE SQL query
It seemed to me the sql I used from that Q&A is logically suitable for my case however I didn't understand the sql.
I am not 100% sure that problematic area is the sql.
I want to correct my code and learn why it fails?
Can you please help?
Best regards
MY CODE
$query = "UPDATE tags SET count = ?
WHERE tag IN(SELECT tag FROM (SELECT tag FROM tags LIMIT ?,1) as u)";
$stmt = $mysqli->prepare($query);
if($stmt === false)
{
trigger_error('SQL Error: ' . $mysqli->error, E_USER_ERROR);
}
$stmt->bind_param("ii", $count, $i);
$i = 0;
foreach ($array as $a) // $array: related values of the `count` column to be updated
{
$count = intval($a);
$stmt->execute();
$i = $i + 1;
}
$stmt->close();

Sql query optimalization

My ch_skills table looks like
uid | skill1 | skill2 | skill3 | skill4 | skill5
1 1 2 2 0 1
2 1 1 2 1 1
3 1 2 3 0 1
My first question: is this correct? I mean would it be better if I made it like this:
uid | skillid | skill_lvl
1 1 1
1 2 2
1 3 2
1 4 0
1 5 1
Everything worked fine until now with the example #1, but now I'm in a trouble with the sql queries. Currently, I'm using 5 different queries to get the level of each skill. I use the following code:
For skill1:
$query = $this->db->prepare("SELECT `skills`.`skill_ID` as `Skill1_id`,
`skill_name`.`skill_name` as `Skill1_name`, `skill_level` as `Skill1_level`,
`skill_price` as `Skill1_price`
FROM `skills`, `skill_name`, `ch_skills`
WHERE `skill_name`.`skill_ID` = `skills`.`skill_ID`
AND `skills`.`skill_ID`= 1
AND `skills`.`skill_level` = `ch_skills`.`skill1`
AND `ch_skills`.`uid` = :uid");
For skill2:
$query = $this->db->prepare("SELECT `skills`.`skill_ID` as `Skill1_id`,
`skill_name`.`skill_name` as `Skill1_name`, `skill_level` as `Skill1_level`,
`skill_price` as `Skill1_price`
FROM `skills`, `skill_name`, `ch_skills`
WHERE `skill_name`.`skill_ID` = `skills`.`skill_ID`
AND `skills`.`skill_ID`= 2
AND `skills`.`skill_level` = `ch_skills`.`skill2`
AND `ch_skills`.`uid` = :uid");
And so on... As you can see, there's only two differences: skill_id = 2, and skill2 as the coulmn's name. Is there any way for querying all the 5 skills in only 1 query? Or would you recommend me anyway to change the table structure?
Note: skills stands for the skill prices, and skill_name for the skill's names.
As the other commenters have suggested, your best choice is to change the table exactly as you proposed.
The biggest reason not to have a wide table like you show in your first example, is that adding a skill means changing the structure of the database, which could break existing queries.
Secondly, as you see when you're trying to query the results, having a single table doesn't even make it easier to work with.
The only possible benefit to a non-normalized table like your example is that it takes up slightly less disk space. But in todays world, disk space should never be your primary concern.
To answer your question about querying the original non-normalized example, however, there are two ways to do it:
Use a union statement which would combine 5 distinct queries together. This is pretty inefficient
Create a table with (in this case) 5 rows (or if you have a Skills table use that). Then join the ch_skills table to that, which should take each row and split it 5 times. See below: (note: I'm assuming for the purposes of this example that skills and skill_name are in a 1:1 relationship and only have 5 records each)
SELECT skills.skill_ID,
skill_name.skill_name,
skill_level as Skill_level,
skill_price as Skill_price
FROM skills
JOIN skill_name on skill_name.skill_ID = skills.skill_ID
JOIN ch_skills
WHERE ch_skills.uid = :uid
AND ((skills.skill_ID = 1 AND skills.skill_level = ch_skills.skill1)
OR (skills.skill_ID = 2 AND skills.skill_level = ch_skills.skill2)
OR (skills.skill_ID = 3 AND skills.skill_level = ch_skills.skill3)
OR (skills.skill_ID = 4 AND skills.skill_level = ch_skills.skill4)
OR (skills.skill_ID = 5 AND skills.skill_level = ch_skills.skill5))

SEARCH PHP and MYSQL

I need mysql code or php to handle some search query
Lets say we have these 5 items in our store.
ID | TYPE | Pattern
1. | Kilner | scissor
2. | Kilner | forcep
3. | Boyd | scissor Small
4. | Boyd | scissor large
5. | Boyd | forcep
6. | Boyd | clamp
Could you help me mysql query to handle below operation
If we search 'boyd' then numbers 3 4 5 and 6 should come up.
If we search 'scissor' then numbers 1 3 and 4 should come up.
If we search 'boyd scissor', numbers 3 and 4 should come up.
If they search' Kilner scissor' then only no 1 should display.
Let me know
thanks
the way to do it in mysql is full text search
SELECT *, MATCH(field) AGAINST ('word1 word2 word3') AS score
FROM table
WHERE MATCH(field) AGAINST('word1 word2 word3')
look this tutorial http://devzone.zend.com/26/using-mysql-full-text-searching/
try this query
SELECT * FROM `table` WHERE `name` LIKE '%$search_var%'
PDO structure
$db = $this->pdo->prepare("SELECT * FROM `table` WHERE `name` LIKE :mysearch");
$db->execute( array("mysearch"=>'%'.$mysearch.'%') );
The query you ask is a bit complex. You want to return matches in both columns, but if both columns match, then single matches have to be discarded.
This means, one way or another, run a query requiring two matches and one requiring one match, comparing the results and returning the appropriate set.
Performance-wise, I believe it is better to run one query that will fetch both, and then handle the results in PHP (you could handle them in MySQL through the use of a superquery).
So:
// We split keywords
$keywords = array_unique(preg_split('/\s+/', $search));
$inset = array();
foreach($keywords as $keyword)
$inset[] = "'".mysql_real_escape_string($keyword)."'";
$sql_in = '('.implode(',', $inset).')';
$query = "SELECT *, IF(type IN $sql_in, 1,0)+IF(pattern IN $sql_in,1,0) AS matches FROM mytable WHERE (type IN $sql_in) OR (pattern IN $sql_in) ORDER BY matches DESC;";
The above uses the discouraged mysql_ functions. Using PDO, that would be:
$keywords = array_unique(preg_split('/\s+/', $search));
// Generate a (?,?,?..?) template as long as $keywords
$sql_in = '('.implode(',', array_fill(0, count($keywords), '?')).')';
$query = "SELECT *, IF(type IN $sql_in, 1,0)+IF(pattern IN $sql_in,1,0) AS matches FROM mytable WHERE (type IN $sql_in) OR (pattern IN $sql_in) ORDER BY matches DESC;";
$st = $db->prepare($query);
$st->execute($keywords);
Note that the above uses exact match, so "Boyd" will retrieve a match with "Boyd", but "Boy" won't. Use the % matching character to change this behaviour.
Now we retrieve a table which is identical to MyTable but has one extra column, "matches", containing either 2 or 1. Can't contain 0 because of the WHERE limitation: one of the two matches must be true and count as 1.
The 2's will be returned first, so we can do
if (!isset($matches))
$matches = $tuple['matches'];
else
if ($tuple['matches'] < $matches)
break;
that is, we save the first (and highest) value, and only accept that value for the subsequent tuples. As soon as an inferior match comes by, we exit the loop and close the cursor.
This may be done in MySQL with
SELECT * FROM ( the above query ) AS newTable
WHERE matches = (
SELECT MAX(matches) FROM ( the above query ) AS tmpTable
);
but it incurs a performance penalty.
$search=array('byod','scissor');
$st=""; $st2="";
foreach($search as $value){$st.="type=%$value% or ";$st2.="pattern=%$value% or ";}
$st2=substr($st2,0,-3);
echo "select * from tablename where $st $st2";

Categories