Slow query with where on joined column - php

I tried to combine 2 queries into 1 by using JOINs
// Query 1
$entity = \DB::select("
SELECT memo_id
FROM entities
WHERE entity_type = 4
AND entity_id = '".$entity_nr."'
LIMIT 1
");
$memo = \DB::select("
SELECT memo
FROM memos
WHERE id = '".$entity[0]->memo_id."'
LIMIT 1
");
// Query 2
$memo = \DB::select('
SELECT memo
FROM memos
JOIN entities ON "memos"."id" = "entities"."memo_id"
WHERE "entities"."entity_type" = 4
AND "entities"."entity_id" IN (?)
LIMIT 1
', [$entity_nr]);
The 2 query's of Query 1 are done in less than a second. Query 2 takes several seconds. If i remove the where clause of Query 2 it executes fast. Using AND "entities"."entity_id" = ? did'nt helped as well.
Howto solve this?
UPDATE 2020-09-27
Refactoring my question. Hopes this makes it clearer. Expected output is to get the memo. There is always only 1 possible match. In the example code im not checking for existence of the result, because it doesn't really add something to the question.
Database which im using is "Actian Zen database".
Table structure
//////////////////////////
// Table: "entity_memo" //
//////////////////////////
CREATE TABLE "entity_memo" (
"entity_type" SMALLINT,
"entity_id" CHAR(15),
"memo_id" INTEGER
);
CREATE UNIQUE INDEX "key0" ON "entity_memo" ( "entity_type", "entity_id", "memo_id" );
CREATE INDEX "key1" ON "entity_memo" ( "memo_id", "entity_type", "entity_id" );
//////////////////////////
// Table: "memos" //
//////////////////////////
CREATE TABLE "memos" (
"id" INTEGER,
"memo" LVAR(32002)
);
CREATE UNIQUE INDEX "key0" ON "memos" ( "id" );
Option 1 (= fast, but 2 queries)
$entity_type = 1;
$entity_id = 'ABC123456';
$entity = \DB::select('
SELECT memo_id
FROM entity_memo
WHERE entity_type = ?
AND entity_id = ?
LIMIT 1
', [$entity_type, $entity_id]
);
$memo = \DB::select('
SELECT memo
FROM memos
WHERE id = ?
LIMIT 1
', [$entity[0]->memo_id]
);
return $memo[0]->memo;
Option 2 (= slow, but 1 query only)
$entity_type = 1;
$entity_id = 'ABC123456';
$memo = \DB::select('
SELECT memo
FROM memos
JOIN entity_memo ON "memos"."id" = "entity_memo"."memo_id"
WHERE "entity_memo"."entity_type" = ?
AND "entity_memo"."entity_id" = ?
LIMIT 1
', [$entity_type, $entity_id]
);
return $memo[0]->memo;

It will be look simpler, if you write it without JOINs.
SELECT ent.unique_id, mem.memo
FROM entities ent, memos mem
WHERE ent.unique_id = mem.unique_id
AND ent.entity_type = 4
AND ent.entity_id = '".$entity_nr."'
But the cause of query working slow is in tables itself. You should to add indexes to PK and FK columns for query to work faster.

Related

Select count with WHERE and OR but specifying a uniqueness

Hi everyone I am in a problem.
I have a table that contains the job requests, which is called: richieste
Another table that contains the appointments which is called: appuntamenti
I have to count how many times the id of a worker, in this case the 57 is present when there is a join between the table: richieste and appuntamenti.
nothing easier.
$q_count_appto_manager = $connessione->prepare("
SELECT count('ID_Richiesta')
FROM richieste
LEFT JOIN appuntamento
ON richieste.ID_Richiesta = appuntamento.appto_id_richiesta
WHERE operato_manager = ?
OR operato_manager_capo_a_manager = ?
OR team_manager = ?
OR team_manager_capo_a_team_manager = ?
AND appuntamento.appto_stato = 'NC'
");
$q_count_appto_manager->bind_param("iiii", $pro,$pro,$pro,$pro);
$q_count_appto_manager->execute();
$r_count_appto_manager = $q_count_appto_manager->get_result();
$rcam=mysqli_fetch_array($r_count_appto_manager);
?>
<?php
if ($rcam[0] == 0) {
echo $rcam[0];
} else {
echo $rcam[0];
}
now the problem begins, the worker with id: 57, can have more positions within the table: richieste, as you can read from the query, in the OR part.
and as you can see from the image
57 ID
so in this case the query will count every time the 57 is present in those positions, generating as output results: 4.
The problem is that I'm asking him to count 57 when he is in those positions, only when there is a join between the two tables, which as a true result should come out: 1
So it doesn't take the where condition into account as unique, but just counts how many times 57 exists in those positions.
How can I specify an attribute of uniqueness, limiting the query to count only when the 57 is in those positions and there is a join between the two tables?
AND has higher precedence than OR, so you need to use parentheses:
WHERE (operato_manager = ? OR operato_manager_capo_a_manager = ? OR
team_manager = ? OR team_manager_capo_a_team_manager = ?)
AND appuntamento.appto_stato = 'NC'
You might find it simpler to use IN with a single parameter:
WHERE appuntamento.appto_stato = 'NC' AND
? IN (operato_manager, operato_manager_capo_a_manager, team_manager, team_manager_capo_a_team_manager)

delete item the order changes php

I have a page that has a column "order". When I add a new item the order should be 1 + max of the item. But if I delete an item between these values like i have 8 9 10 , if I delete item with 9 the new item will have 11 value and I will have 8 10 11.But I want 8 9 10. How can I do this in php? should I create a function? It works to add a new item with the max+1, but if I delete is not good.
My code:
$queryMaxOrd = mysql_query("select max(ord) from article where user_id=" .$_SESSION['userId']);
$row = mysql_fetch_row($queryMaxOrd);
$valueMaxOrd = $row[0] + 1;
$_SESSION['a'] = $valueMaxOrd;
and the insert query....
Any ideas?
First thing to consider is removing mysql_* and start using mysqli_* or pdo. Your code is vulnerable to sql injections.
With your code structure you have you adding each order as a new record to your table, so when you have 5 orders with one users your table will look like this.
user: Adiiia | order: 5
user: Adiiia | order: 3
user: Adiiia | order: 7
user: Adiiia | order: 2
user: Adiiia | order: 9
when you query the database you are saying to mysql: get me the maximum number of the record i have with user: Adiiia. The result should be 9
If you want to count the sum of the records you should use sum
select sum(ord) from article where user_id='".$_SESSION['userId']."'
The result should be 26
The best thing you can do is by creating a new table orders_sum
The structure should like like this.
Total_orders | User
when a user have a new order you can update the table by adding one or else if have a order removed then removing minus one order.
Something like this.
Total_orders | User
5 | Adiiia
When you want to update:
select Total_orders from orders_sum where User='Adiiia'
Find how many orders the user have by fetching the table.
$orders = $row['Total_orders'];
$plus_orders = $row['Total_orders'] + 1;
update orders_sum set Total_orders='".$plus_orders."' where user='Adiiia'
When you want to delete:
select Total_orders from orders_sum where User='Adiiia'
Find how many orders the user have by fetching the table.
$orders = $row['Total_orders'];
$minus_orders = $row['Total_orders'] - 1;
update orders_sum set Total_orders='".$minus_orders."' where user='Adiiia'
Lets say article table having primary key article_id then after delete any article for user_id. Fetch all the article_id for the user_id. Then update the order for all the article_id for that user.
$queryArticle = mysql_query("select article_id from article where user_id=" .$_SESSION['userId']." order by article_id asc");
$allArticle = [];
while($rowArticle = mysql_fetch_assoc($queryArticle)){
$allArticle[] = $rowArticle['article_id'];
}
$query = "UPDATE article SET ord = (CASE order ";
foreach($allArticle as $order => $article_id) {
$query .= " WHEN {$article_id} THEN {$order}";
}
$query .= " END CASE) WHERE article_id IN (" . implode(",", $allArticle) . ")";

How could I execute this basic table query in PHP?

Suppose I have a table TABLE:
NAME ID ...
m -1 ...
f -1 ...
g -1 ...
b -1 ...
z -1 ...
And I want to turn it into:
NAME ID ...
f 1 ...
g 2 ...
m 3 ...
b -1 ...
z -1 ...
You probably get the idea:
select the first 3 rows from the original table (preserving order)
order selected rows by the NAME column.
update selected rows' IDs with their position in the new table (keeping the remaining unselected rows in their original positions).
So (m, f, g) got sorted to (f, g, m) and (b, z) remained (b, z).
Here's how I am trying to do it in PHP:
$count = 0;
$query = "UPDATE TABLE SET ID = $count:= $count + 1 ORDER by NAME DESC LIMIT 3";
mysqli_query($con, $query);
But I don't think I can just go ahead and increment a counter and store its value like that. Any advice?
You can try this :
$limit = 3;
for($count = 0 ; $count < $limit;$count++ ){
$query = "UPDATE TABLE SET ID = $count + 1 WHERE ID = '-1' ORDER by NAME DESC";
mysqli_query($con, $query);
}
$query = "UPDATE TABLE SET ID = '-1' WHERE ID > $limit ORDER by NAME DESC";
mysqli_query($con, $query);
In the above logic :
In the final loop, all the IDs are set to $limit
However the update command outisde the loop will set back IDs to -1 again
First, you can quickly query for the first 3 rows in the table and get the name property only and assign the value in an array.
$sql = "select name from table order by name limit 3"
$query = $mysqli->query($sql);
Now let's construct a helper array:
while ($row = $mysqli->fetch_assoc()) {
$a[] = $row['name'];
}
Now just structure the queries:
foreach($a as $id => $name) {
$query = "update table set id={$id+1} where name='$name' limit 1";
// execute the query
}
Note that I assume that the name is unique so I added the limit 1 directive to tell it stop looking for rows to update once it has found a row.
Also, don't forget that array keys are counting starting from 0, hence we are adding 1 to the $id in the loop.
There may be more elegant solutions but this one is rather easy to understand and use.
In MySQL:
SET #row_number = 0;
update TABLE d
join
(
select
NAME,
#row_number:=#row_number+1 as ID,
from
(select NAME from TABLE limit 3) t
order by
NAME asc
) s on s.NAME = d.NAME
set d.ID = s.ID;
SQLFiddle: http://sqlfiddle.com/#!9/dffecf/1
This assumes NAME is your unique key, otherwise likely best to replace with an Identity column in your table and use that for the update.
This approach may require some syntax changes depending on your DB engine. By doing this in SQL, we only make one pass at the DB. Not a huge deal to iterate in multiple passes with PHP if you're only updating three records, but if it was a 1000, etc.

Unique value count of comma separated field (PHP - MySQL)

I have mysql table that looks like this:
id place interest
1 place1 a,b,c
2 place2 c,d,e
3 place1 a,e
4 place2 f
5 place2 f
6 place3 g,h
I need to get unique "place" and "interest" values sorted as per the count.
So, the output for "place" would be
place2(3)
place1(2)
place3(1)
So, the output for "interest" would be
a(2)
c(2)
e(2)
f(2)
b(1)
d(1)
g(1)
h(1)
is there a way to do this in PHP-Mysql?
So, far I have been able to get simple column data
SELECT place,
COUNT( * ) AS num
FROM testtab
GROUP BY place
ORDER BY COUNT( * ) DESC
As mysql is not able to hold arrays, its better to build a new table like this:
interest_id interest_name
1 a
2 b
and another one to keep the relations:
pk id interest_id
1 1 1
2 1 2
which this id is the id of the records in your main table.
With having this, you can easily use:
select count(*) from THIRD_TABLE where id = YOUR_ID
You can do this.
$place = array();
$interests = array();
foreach($rows as $row){
if (!isset($place[$row["place"]])){
$place[$row["place"]] = 0;
}
$place[$row["place"]]++;
$ints = explode(",", $row["interests"]);
foreach($ints as $int){
if (!isset($interests[$int])){
$interests[$int] = 0;
}
$interests[$int]++;
}
}
This will give you the two arrays keyed off of the relevant field with the value being the count. If this is going to be a common action in your application it would make more sense to normalize your data as suggested by AliBZ.
This is for the first result you need
SELECT place,COUNT(interest)
FROM `testtab`
GROUP by place
ORDER BY COUNT(interest) desc
can do this :
$inst_row = '';
foreach($rows as $row){
$inst_row .= $row['interests'];
}
$inst_values = explode(',', $inst_row);
$inst_count = array_count_values($inst_values);
// $inst_count will return you count as you want ,print_r it and format it accordingly

MySQL Count in PHP while loop only returns one result then Null

Ok, so I have some MySQL tables as follows:
Buildings
Building-ID Building-Name
===========----=============
1 Building-1
2 Building-2
3 Building-3
4 Building-4
Building-1
Mroom State
=====----======
1 Booked
2 Empty
3 Empty
4 Empty
Building-2
Mroom State
=====----======
1 Booked
2 Empty
3 Empty
4 Empty
And a query in PHP as follows (Ignore the hard coded while, I've simplified the code a bit):
$sql = "select * from Buildings";
$result = mysql_query ($sql) or die(mysql_error());
while ($row = mysql_fetch_array($result))
{
$building[] = $row['ward_name'];
}
$v1 = 0;
while ($v1 < 4)
{
$sql = "SELECT COUNT(*) FROM `$building[$v1]` WHERE state = 'Empty'";
$result = mysql_query($sql) or die(mysql_error());
$count = mysql_result($result, 00);
var_dump($count[$v1]);
$v1 = $v1 + 1;
}
To my way of thinking this should create an array of the buildings contained in the "Buildings" table, start a loop, load the building name from the array and provide a row count for the table of how many rows contain "Empty" in the state column. What it actually does is provide a count for the first table and then provides "NULL" for the rest.
I'd appreciate any help you can give me.
Cheers!
What about changing your data model?
Table buldings can be kept as is:
Buildings
Building-ID Building-Name
===========----=============
1 Building-1
2 Building-2
3 Building-3
4 Building-4
New table:
Rooms
Building-ID Mroom State
===========-=====-=====
1 1 1
1 2 0
2 1 0
State 0 = Empty, State 1 = Booked
Then use a join with group by:
select count(*) from buildings b inner join rooms r on r.bid = b.id where r.state = 0 group by b.id;
Then you will get a row for each building with the count of empty rooms. You won't need a table for each building.
This does noit make sense:
$count = mysql_result($result, 00);
var_dump($count[$v1]);
you mean to write:
$count[$v1] = mysql_result($result, 00);
var_dump($count[$v1]);
Also do not use several tables with names matching columns of other tables.
You can use one table with a primary key that spans two columns instead, for example create primary key on($buildingid,$roomid)
so that the table has columns $buildingid,$roomid, and $state.
mysql_result() returns a string, not an array.
Modify the code and check that now it works as expected.
var_dump($count);

Categories