What I'm trying to do here, is I want to output the count of the id and user_id columns from items, by users which have the rank 1.
Let's say there's 8 rows in items which have a base_item of 400, and these 8 rows, where 2 rows of them has a user_id of 4 and the existing left has a user_id of 6 The output should then be:
$countItems = 8 and $countUsers = 2.
$stmt = $conn->pdo->prepare("
SELECT COUNT(*),`user_id` FROM `items`
INNER JOIN `users`
ON `users`.`id` = `items`.`user_id`
WHERE `base_item` = :i AND `rank` = 1
GROUP BY `user_id`,`items`.`id`
");
$data = array(':i' => '400'); // item_id in `values` table
$stmt->execute($data);
if($inv = $stmt->fetch($db->FETCH_ASSOC)) {
$countItems = $inv['id'];
$countUsers = $inv['user_id'];
}
I have tried several methods, but I still keep getting output 2 and 2 even though the results should be 8 and 2.
Table Info for values:
Table Info for items:
First of all you never want to fetchAll() and discard the data just to count the number of rows. There is a better way.
SELECT COUNT(*) FROM `items` WHERE `base_item` = :item AND `user_id` = :uid
Secondly, you should try to avoid a nested loop whenever possible because nested loops don't scale very well. And the solution is to use a join or a subquery. You haven't posted your table structures, so this query is untested.
SELECT COUNT(*), user_id FROM `items` INNER JOIN users
ON users.id = items.user_id
WHERE `base_item` = :item and rank =1 GROUP BY user_id
One query instead of two, a lot less number of lines of code.
Problem is that you're replacing $smst withing the foreach loop.
Use other variable name, for example:
$stmt->execute();
foreach($stmt as $user) {
$stmt2 = $conn->pdo->prepare("SELECT `id`,`user_id` FROM `items` WHERE `base_item` = :item AND `user_id` = :uid");
$stmt2->bindParam(':item', $cmsval['item_id'], $db->PARAM_INT);
$stmt2->bindParam(':uid', $user['id'], $db->PARAM_INT);
Well, all I actually had to do, was remove GROUP BY and add COUNT DISTINCT instead, like following:
$stmt = $conn->pdo->prepare("
SELECT COUNT(DISTINCT `user_id`) as `uid`,
COUNT(`items`.`id`) as `id`
FROM `items`
INNER JOIN `users`
ON `users`.`id` = `items`.`user_id`
WHERE `base_item` = :i AND `rank` = 1
");
$stmt->bindParam(':i', $cmsval['item_id'], $db->PARAM_INT);
$stmt->execute();
if($inv = $stmt->fetch($db->FETCH_ASSOC)) {
$countItems = $inv['id'];
$countUsers = $inv['uid'];
}
Related
I have 3 tables Order, CheckoutStatus, and Statuses. There is a foreign key in Order and CheckoutStatus that references the Statuses table.
I need to join CheckoutStatus to Order linked by the PO column and I need to join Statuses to Order and to CheckoutStatus.
Here is the data in the tables
Order table
`PO` = 123456
foreign key `Statuses_id` = 2
CheckoutStatus
`PO` = 123456
foreign key `Statuses_id` = 0
Statuses
`id` 0 = Complete
`id` 2 = Completed
How do I write my SQL statement so that I can have a result like this.
Order
123456
Completed
CheckoutStatus
123456
Complete
This SQL statement I'm using does not display anything unless I remove one of the JOIN Statuses section of the statement.
SELECT * FROM `Order` JOIN `Statuses` ON Statuses.id = Order.Statuses_id JOIN `CheckoutStatus` ON Order.PO = CheckoutStatus.PO JOIN `Statuses` ON Statuses.id = CheckoutStatus.Statuses_id
You have two JOINs on table Statuses. You need to use table aliases to distinguish the two relationships :
SELECT
`Order`.PO,
s1.Status,
s2.Status
FROM
`Order`
JOIN `Statuses` s1 ON s1.id = Order.Statuses_id
JOIN `CheckoutStatus` ON Order.PO = CheckoutStatus.PO
JOIN `Statuses` s2 ON s2.id = CheckoutStatus.Statuses_id
this can get a little bit confusing you can split the query and on the basis of its fetched result, you can fetch further more
$sql4 = "SELECT * FROM `Order` JOIN `Statuses` ON Statuses.id =
Order.Statuses_id JOIN `CheckoutStatus` ON somthing"
$result4 = mysqli_query($conn, $sql4);
if (mysqli_num_rows($result4) > 0)
{
while($row3 = mysqli_fetch_assoc($result4))
{
$sql5='SELECT * FROM //the result of before joining other two '
}
}
something like this
Here is what I have (i am including only the relevant cells to each table)
table1:
- user_id
table2:
- id
- manager_id
... and I have a PHP variable
$manager_id
So, what I am trying to do is get a COUNT(*) of how many records exist in table1, but the record should only be counted if table1.user_id (which is the table2.id) has a table2.manager_id == $manager_id
So, the only way I know how to do this would be to do something like this in PHP (which is wildly inefficient):
$query = "SELECT user_id FROM table1 WHERE {my where clause}";
// execute query and place into $item[] array (not shown for brevity)
foreach ( $item as $user_id ) {
$query = "SELECT manager_id FROM table2 WHERE id = '{$user_id}';
// execute query, place each item into $row
if ( $row['manager_id'] == $manager_id ) {
// tick up count by 1
}
I am fairly certain there is a way to do this purely in SQL, but I am at a loss.
You could try the below query:
select count(1) from table1 t1, table2 t2 where t1.user_id = t2.id and t2.manager_id = ?
you need to set your where clause to have this condition i.e.
SELECT Count(user_id) FROM table1 where
table1.user_id = table2.id
AND Convert('Varchar', table2.manager_id) = $manager_id
I assume your php variable $manager_id has a string
if it is not a number then there is no need to convert the sql column (table2.manager_id) to varchar.
You can do this with one SELECT statement:
SELECT COUNT(*)
FROM table1
WHERE user_id IN (
SELECT id
FROM table2
WHERE manager_id = <your-value-of-manager-id>
)
In case you need to use the data and want to count on PHP side you can use the following SELECT statement using a LEFT JOIN:
SELECT table1.*
FROM table1 LEFT JOIN table2 ON table1.user_id = table2.id
WHERE table2.manager_id = <your-value-of-manager-id>
To execute the SELECT statements you should use prepared statements (using PDO or mysqli).
example using the first query:
$sql = 'SELECT COUNT(*) FROM table1 WHERE user_id IN (SELECT id FROM table2 WHERE manager_id = ?)';
$sth = $dbh->prepare($sql);
$sth->execute([$manager_id]);
$rows = $sth->fetchAll();
if (count($rows) === 1) {
echo $rows[0][0];
}
example using the second query:
$sql = 'SELECT table1.* FROM table1 LEFT JOIN table2 ON table1.user_id = table2.id WHERE table2.manager_id = ?';
$sth = $dbh->prepare($sql);
$sth->execute([$manager_id]);
$rows = $sth->fetchAll();
echo count($rows);
I need to run a query like below
SELECT `loginname`, `id`, `locality`, `wherefrom`
FROM `Volley` JOIN `register` ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%'
from this result i need to take id and pass it to the other query to check that id exist in another table like below.
SELECT IF(EXISTS(SELECT *
FROM `likes`
WHERE Volleyid = '20'),1,0)
Can i combine this into a single query? For the first one i may get more that 1 rows. I tried and got the results of first query into a array but i am not able to get that particular value from array.
$data = array();
for ($x = 0; $x < mysqli_num_rows($query1); $x++)
{
$data[] = mysqli_fetch_assoc($query1);
}
SELECT IF(EXISTS(SELECT * From
(
SELECT `loginname`,
`id`,
`locality`,
`wherefrom`,
Volleyid
FROM `Volley`
JOIN `register`
ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%'
) AS T1
WHERE Volleyid = '20'),1,0)
You can use a subquery with IN clause (i think what you what it ś that Volleyid it's equal to id):
SELECT IF(EXISTS(SELECT *
FROM `likes`
WHERE Volleyid IN
(SELECT `id`
FROM `Volley` JOIN `register` ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%')),1,0)
If you want to get loginname, id, locality, wherefrom columns in the first query alongside with the 1 or 0 flag checked against table likes in single result set. You can write query that looks like this:
SELECT v.*, IF(l.Volleyid IS NOT NULL, 1, 0) as existFlag
FROM
(SELECT `loginname`, `id`, `locality`, `wherefrom`
FROM `Volley`
JOIN `register` ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%') AS v
LEFT JOIN
(SELECT DISTINCT Volleyid
FROM `likes`) l ON v.id = l.Volleyid;
I have three tables and I need to search in the first one, it there isn't any result, then search into second one and so on ..!
Here is my code:
// connecting to database
$stm1 = $this->dbh->prepare(" select * from table1 where col = :name; ");
$stm1->bindValue(":name", $name, PDO::PARAM_STR);
$stm1->execute();
$which_table = "table1";
// the result of searching into table1 is zero, then search into table2
if (!$stm1->rowCount()) {
$stm2 = $this->dbh->prepare(" select * from table2 where col = :name; ");
$stm2->bindValue(":name", $name, PDO::PARAM_STR);
$stm2->execute();
$which_table = "table2";
// the result of searching into table2 is zero, then search into table3
if (!$stm2->rowCount()) {
$stm3 = $this->dbh->prepare(" select * from table3 where col = :name; ");
$stm3->bindValue(":name", $name, PDO::PARAM_STR);
$stm3->execute();
$which_table = "table3";
// the result of searching into table3 is also zero
if (!$stm3->rowCount()) {
$which_table = 'none of them';
}
}
}
My code works as well, But it is slow, How can I optimize it and make it faster? As you see, there is three separated query and multiple if-statement .. How can I reduce them? Generally How can I improve that code? Can I do that using pure-sql?
If you code is slow, then you probably just need indexes:
create index idx_table1_col on table1(col);
create index idx_table2_col on table2(col);
create index idx_table3_col on table3(col);
With indexes, you can also phrase the query as a single statement, assuming the columns in the tables are the same:
select t1.*
from table1 t1
where t1.col = :name
union all
select t2.*
from table2 t2
where t2.col = :name and
not exists (select 1 from table1 t1 where t1.col = :name)
union all
select t3.*
from table3 t3
where t3.col = :name and
not exists (select 1 from table1 t1 where t1.col = :name) and
not exists (select 1 from table2 t2 where t2.col = :name);
This is a more complex query, but your code would only require a single query. And, with indexes, it should be very fast.
My code works as well, But it is slow, How can I optimize it and make it faster?
Add index for the column col
By the way, you may add limit 1 to your queries. It'll help you you have zillions of values in your tables to match
I have a nested loop in my PHP code that is taking a long time and wondered of there was a way to speed this up:
$sql = "SELECT * FROM table_1";
$result = mysqli_query($sql);
while($row = mysqli_fetch_array($result)){
$sql2 = "
SELECT count(*)
FROM user_modules
WHERE
begin_ymdhi >= ".$date_from." AND
begin_ymdhi <= ".$date_to." AND
(
completed_ymdhi IS NULL OR
completed_ymdhi = ''
) AND
user_id = ".$row['user_id']." AND
task_id = ".$row['task_id']." AND
module_discon = 'N'
";
}
The outer query gets 1000 rows, the inner has to count across 10,000 rows - the run-time is around 115 seconds ! Is there any way to improve this method, either using a different technique or combined SQL query?
Don't use nested queries, combine them into a single query with a join:
SELECT t1.*, COUNT(u.user_id) ct
FROM table_1 t1
LEFT JOIN user_modules AS u ON u.user_id = t1.user_id AND u.task_id = t1.task_id
AND u.begin_ymdhi BETWEEN '$date_from' AND '$date_to'
AND u.module_discon = 'N'
GROUP BY t1.user_id, t1.task_id
Are the task_id's unique? if so, the most straight forward would be something like:
$sql2 = "
SELECT count(task_id) AS TaskCount
FROM user_modules
WHERE
begin_ymdhi >= ".$date_from." AND
begin_ymdhi <= ".$date_to." AND
(
completed_ymdhi IS NULL OR
completed_ymdhi = ''
) AND module_discon = 'N'
group by user_id
";
$result = mysqli_query($sql2);
SELECT user_modules.user_id, user_modules.task_id, count(*)
FROM user_modules LEFT JOIN table_1 USING (user_id, task_id)
WHERE
begin_ymdhi >= ".$date_from." AND
begin_ymdhi <= ".$date_to." AND
module_discon = 'N' AND
(
completed_ymdhi IS NULL OR
completed_ymdhi = ''
)
GROUP BY user_modules.user_id, user_modules.task_id
Append EXPLAIN before that entire SELECT statement (i.e EXPLAIN SELECT count(*)...) and MySQL will give you a run-down on what the select is doing.
Make sure the begin_ymdhi field is indexed properly. SHOW INDEX FROM table_2 to see.