Moving the workload from PHP to MySQL - php

I currently have two queries that are executed in order. The first query pulls a few rows from a database and the 2nd query is executed using one value from those rows. I am only interested in the rows from the 1st query where I know the 2nd query is going to return a value higher than 0. I could potentially have thousands of rows in my first table, so I rather do the filtering in MySQL and not in PHP, I would assume it is faster.
The first query is as following
SELECT
*
FROM
wp_invoices
WHERE
deleted = 0
The 2nd query is as following:
SELECT COUNT(*) FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id = %d
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities' AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)
Both queries work separately, this is not the problem. Currently, the %d in the 2nd query is replaced by PHP, which inserts the wp_invoices.id value for each row.
Ideally, I would like a query that returns only the rows from wp_invoices, where the 2nd query would return a value higher than 0. At this point I am not interested in the rows of the 2nd query, only the amount of rows found.
My table structures are as following: http://sqlfiddle.com/#!9/466cb
This is what I do in PHP, currently, to handle all this. Please keep in mind that this code was written to clarify this post, I am using deprecated MySQL functions because I don't write PHP that often anymore and haven't really looked into mysqli and I haven't checked if this code actually runs. It should give you a better indication of what I am currently doing.
<?php
/* Example code. I am not using mysql_ functions in my own solution, I just
know these functions by heart. Assume a database connection is already
established */
/* Refer to this as QUERY 1 */
$result = mysql_query("SELECT * FROM wp_invoices WHERE deleted = 0");
/* Loop over all invoices (this is not an invoice 'record'. These are
the invoice definitions, not the actual instances that people received */
while($row = mysql_fetch_assoc($result)) {
/* Refer to this as QUERY 2 */
$count_result = mysql_query(sprintf("
SELECT
COUNT(*) AS count
FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id = %d
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities'
AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)", $row['id']));
if(mysql_result($count_result, 0) > 0) {
/* Here I would pull all data I would need for this invoice. $row
contains all the information about the invoice, $invoice_record
will contain all the information for this particular user about
this invoice*/
$invoice_records = mysql_query(sprintf("
SELECT
A.ID AS id,
A.display_name,
C.id AS received,
DATE_FORMAT(C.received_on, '%%d-%%m-%%Y') AS received_on,
C.status,
A.user_email,
C.price,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'first_name'
AND user_id = A.ID) AS first_name,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'tssnvgsl'
AND user_id = A.ID) AS insertion,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'last_name'
AND user_id = A.ID) AS last_name
FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id = %d
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities'
AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)", $row['id']));
while($invoice_record = mysql_fetch_assoc($invoice_records)) {
/* Perform logic to see if this person should receive a reminder */
}
}
}
?>
What I attempt to achieve, though:
<?php
$result = mysql_query(/* here a query that pulls all rows from QUERY 1
where QUERY 2 would have returned a count higher than 2 */);
while($row = mysql_fetch_assoc($result)) {
/* Now here I should be 100% sure that every $row has at least one
"pending" invoice record, that has not yet been paid and has a
status of < 100 */
}
?>

Alright. I have been racking my brain staring at your process and I think it is starting to make sense. If I am not mistaken you should be able to eliminate both query1 and query2 and move the logic into the LEFT JOIN of your third query like such.
SELECT
A.ID AS id,
A.display_name,
C.id AS received,
DATE_FORMAT(C.received_on, '%%d-%%m-%%Y') AS received_on,
C.status,
A.user_email,
C.price,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'first_name'
AND user_id = A.ID) AS first_name,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'tssnvgsl'
AND user_id = A.ID) AS insertion,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'last_name'
AND user_id = A.ID) AS last_name
FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id IN (SELECT id FROM wp_invoices WHERE deleted = 0)
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities'
AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)"
This query, instead of having to be ran every time for every record and the $row['id'] added via php within your LEFT JOIN I am using MySQL IN functionality with a subselect. This gives you the ability to join only on records from wp_invoices which where deleted.

Related

MySQL query with multiple left joins and different WHERE clauses for each

I'm modifying a query that previously left joined two tables, but now needs to add a third, and I'm struggling with some of the conditions being applied.
This is the original query:
$sql = "SELECT COUNT(act.rowId) q4Act
FROM accounts a
LEFT JOIN leads l ON a.accountId = l.accountId
LEFT JOIN activities act ON act.leadId = l.leadId AND act.classification = 'Positive' AND activityDate >= '2016-10-31' AND activityDate < '2017-02-01'
WHERE a.accountId = '$id'
GROUP BY a.accountName ";
This worked fine, ensuring that the only results from the activities table were meeting the three AND conditions and the accountId was the one being queried.
However, I split two tables and wanted to update this query. My first attempt was this next query, where I added the new left join, removed a condition from the first one and added it to the second one. This, however, returns totally different results.
$sql = "SELECT COUNT(act.rowId) q4Act
FROM accounts a
LEFT JOIN leads l ON a.accountId = l.accountId
LEFT JOIN activities act ON act.leadId = l.leadId AND activityDate >= '2016-10-31' AND activityDate < '2017-02-01'
LEFT JOIN interestingMoments im ON im.rowId = act.imId AND im.classification = 'Positive'
WHERE a.accountId = '$id'
GROUP BY a.accountName ";
My next attempt was to move the condition on the new table below the joins like this:
$sql = "SELECT COUNT(act.rowId) q4Act
FROM accounts a
LEFT JOIN leads l ON a.accountId = l.accountId
LEFT JOIN activities act ON act.leadId = l.leadId AND activityDate >= '2016-10-31' AND activityDate < '2017-02-01'
LEFT JOIN interestingMoments im ON im.rowId = act.imId
WHERE a.accountId = '$id'
AND im.classification = 'Positive'
GROUP BY a.accountName ";
The above returns the same results when there's a positive count, though it doesn't return a 0 count like the first query did. This isn't a big deal, but I'd like to see if this is the proper way to construct this query.
Is this the best way, or should I be going about it a different way?
Placing the constraint on the JOIN versus in the WHERE clause really creates no difference. If the tables is huge, it may affect time to run the SELECT, but that difference is likely small and can still probably be mitigated by indexes.

PHP/MYSQL counting orders

Here is database table:
$sql[2] = "SELECT u.* , oi.* , COUNT(oi.user_id) AS count
FROM users u, order_items oi
WHERE u.id=oi.user_id ";
$result3= mysqli_query($conn,$sql[2]) or die(mysqli_error());
if (mysqli_num_rows($result3) > 0) {
while ($record = mysqli_fetch_array($result3)) {
echo $record['count'];
}
}
I want to count how much order have every user. Example: Like Thomas have 3 order, but my code is writing 4, i want to write Thomas (3), Gracian(1). Any idea how to fix it ?
Use this query:
SELECT u.id,
COUNT(oi.user_id) AS orderCount
FROM users u
LEFT JOIN order_items oi
ON u.id = oi.user_id
GROUP BY u.id
The reason we count user_id from the order_items table is because of the edge case where a given user has no orders. In this case, we want to make sure that his count would appear as zero. The COUNT function ignores NULLs, which is what we want.
There is another way to perform the sql query using subqueries:
SELECT id,
email,
address,
name,
(SELECT count(user_id) FROM order_items WHERE user_id = users.id) AS orderCount FROM users;

getting data from four tables with tight WHERE clause

i need help getting data from different tables and insert into other different table Here are the Queries
"SELECT commentID, date, comment, subject, parentID, aBUserID FROM comments WHERE status = 'APPROVED'"
"SELECT topicID, subForumID, aBUserID, lastPostID, views, replies, startDate FROM topic WHERE status = 'APPROVED' AND topicID = $parentid";
// $parentID need to be matched from above query parentID,
"SELECT userName FROM users WHERE aBUserID = $cmtaBUserID";
// $cmtaBUserID = aBUserID from first query
"SELECT userName FROM users WHERE aBUserID = $topicaBUserID";
//$topicaBUserID = aBUserID from second query
Last 2 queries are from same table but using different where clause
i used different inner join left join from solutions posted here but non of these worked for me stuck since last 2 weeks please help
PS data from all above Queries will be inserted to a single table i need these to be combined so i can have them all in one place
If you want to perform the operation in same query use 'OR'
"SELECT userName FROM users WHERE aBUserID = $cmtaBUserID OR aBUserID = $topicaBUserID";
Please try this
SELECT userName from users where aBUserID IN(SELECT aBUserID FROM comments WHERE status = 'APPROVED')
Couldn't test it but Maybe this is what you are looking for.
SELECT c.commentID, c.date, c.comment, c.subject, c.parentID, c.aBUserID,
t.topicID, t.subForumID, t.aBUserID, t.lastPostID, t.views, t.replies, t.startDate,
u.userName
FROM
comments c
left outer join topic t on t.topicID = c.parentID
left outer join users u on u.aBUserID = c.aBUserID and u.aBUserID = t.aBUserID
WHERE
c.status = 'APPROVED' and t.status = 'APPROVED';
try this:
SELECT
comment.[commentID],
comment.[date],
comment.[comment],
comment.[subject],
comment.[parentID],
comment.[aBUserID],
commentuser.[userName],
topic.[topicID],
topic.[subForumID],
topic.[aBUserID],
topic.[lastPostID],
topic.[views],
topic.[replies],
topic.[startDate],
topic.[userName]
FROM comments comment
LEFT OUTER JOIN users commentuser
ON commentuser.aBUserID = comment.[aBUserID]
LEFT OUTER JOIN
(
SELECT
t.[topicID],
t.[subForumID],
t.[aBUserID],
t.[lastPostID],
t.[views],
t.[replies],
t.[startDate],
u2.[userName] --user from users table joined to topics table
FROM topic t
LEFT OUTER JOIN users u
ON u.aBUserID = t.[aBUserID]
WHERE t.[status] = 'APPROVED'
) topic
ON topic.topicID = comment.parentID
WHERE comment.[status] = 'APPROVED'

How to make a fancy mysql join that can join three tables and detect if one table DOESNT have my item

php mysql query
I have multiple linked tables - I also have a table that only creates and entry if certian conditions exist so I would like to add that into my query to avoid having to go through thousands of query searches looking for this special case
here is my current query
$query = "SELECT a.UUID FROM contract a
INNER JOIN geoPoint b ON a.customer_UUID = b.customerUUID
WHERE b.garcom_UUID = '$garbCom'
AND b.city_UUID = '$city'";
I then go through each item that was returned (in the thousands)
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
{
$sentdata = getothertable($row['UUID']); //checks if the item is in the table
$sent = $sentdata ['senttoGarcom'];
if($sent == 0) //if it wasn't found add it to my list
{
array_push($Contracts,$row['UUID']);
}
}
instead of all that I would like to just make it one query - pseduo code something like this
$query = "SELECT a.UUID FROM contract a
INNER JOIN geoPoint b ON a.customer_UUID = b.customerUUID
INNER JOIN contract_sales c ON a.UUID = c.contractUUID
WHERE b.garcom_UUID = '$garbCom'
AND b.city_UUID = '$city' AND c.DOESNOTEXIST";
this way I dont have to return thousands I will only be returned the few that are not yet in the contract_sales table and I can go about my business...
Appreciate any help!
just check for NULL rows of c with a outer join
$query = "SELECT a.UUID FROM contract a
INNER JOIN geoPoint b ON a.customer_UUID = b.customerUUID
LEFT OUTER JOIN contract_sales c ON a.UUID = c.contractUUID
WHERE b.garcom_UUID = '$garbCom'
AND b.city_UUID = '$city' AND c.contractUUID IS NULL ";
I think this is a left outer join problem
Have a look at this example. You specifically need to have a check for a null in a column in the table which you want to find the missing row rof
mysql left outer join
Sounds like a NOT EXISTS correlated subquery is what you need:
$query = "SELECT a.UUID FROM contract a
INNER JOIN geoPoint b ON a.customer_UUID = b.customerUUID
WHERE b.garcom_UUID = '$garbCom'
AND b.city_UUID = '$city'
AND NOT EXISTS (SELECT 1
FROM contract_sales c
WHERE c.contractUUID = a.UUID)";

PHP / MySQL - Confusing Query

Im trying to construct a query that goes over 3 tables and im COMPLETELY stumped ... my knowledge limit is basic 1 table query and i need some help before i stick my head in a blender.
I have the following query
SELECT * FROM internalrole WHERE introle = $imarole
Im fine with that part .. its the next thats getting me all stressed.
That query returns the following columns ( id, user_id, introle, proven, used )
What i then need to do is take the user_id from the results returned and use it to get the following
SELECT * FROM users WHERE id = user_id(from previous query) AND archive = 0 and status = 8
I need to put that into 1 query, but wait, theres more .... from the results there, i need to check if that user's 'id' is in the availability table, if it is, check the date ( column name is date ) and if it matches todays date, dont return that one user.
I need to put all that in one query :S ... i have NO IDEA how to do it, thinking about it makes my head shake ... If someone could help me out, i would be eternaly grateful.
Cheers,
Use INNER JOIN, which links tables to each other based on a common attribute (typically a primary - foreign key relationship)
say an attribute, 'id', links table1 and table2
SELECT t1.att1, t2.att2
FROM table1 t1
INNER JOIN table2 t2
ON t1.id = t2.id --essentially, this links ids that are equal with each other together to make one large table row
To add more tables, just add more join clauses.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
LEFT JOIN availability a
ON ir.user_id = a.user_id
AND a.date = CURDATE()
WHERE ir.introle = $imarole
AND a.user_id IS NULL /* User does NOT exist in availability table w/ today's date */
EDIT: This second query is based on the comments below, asking to show only users who do exist in the availability table.
SELECT u.*
FROM internalrole ir
INNER JOIN users u
ON ir.user_id = u.id
AND u.archive = 0
AND u.status = 8
INNER JOIN availability a
ON ir.user_id = a.user_id
WHERE ir.introle = $imarole
Hmm, maybe something like this
SELECT * FROM users WHERE id IN (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8;
A handy thing for me to remember is that tables are essentially arrays in SQL.
HTH!
Nested queries are your friend.
SELECT * FROM users WHERE id in (SELECT user_id FROM internalrole WHERE introle = $imarole) AND archive = 0 and status = 8
Alternatively joins:
SELECT * FROM users INNER JOIN internalrole ON users.id = internalrole.user_id WHERE internalrole.user_id = $imarole AND users.archive = 0 and users.status = 8

Categories