Query two tables for different/separate values - php

I have two tables:
task
id name dueDate completed projectID
project
id name dueDate completed
I need to query both tables for rows with the same data. I tried doing a sample statement just looking for rows with completed=0, but never got any results. I think it has to do with using OR instead of AND, but it's just a little above my level right now...Any ideas?
TO CLARIFY, I'm not looking for duplicate rows, I'm looking for 'all tasks and projects with completed = 0'
The query is:
SELECT * FROM "task" t, "project" p WHERE t.dueDate = "2012-08-17" OR p.dueDate = "2012-08-17" AND t.completed = 0 OR p.completed = 0
I did manage to get one of the answers' code to work, however I realized that my entire app was written to talk to one table, and that it would be much easier to just combine the task and project table and use an isProject column to differentiate projects from tasks. This also adds the ability to nest projects inside of projects, because projects will now have a projectID column as well.
In the end, KISS prevails...
Thanks for all the help! I will mark the answer that worked, even though I won't be using it.

Try using parenthesis.
SELECT * FROM "task" t, "project" p WHERE (t.dueDate = "2012-08-17" OR p.dueDate = "2012-08-17") AND (t.completed = 0 OR p.completed = 0)

If You want only values matches from both tables with completed=0 from dueDate='2012-08-17':
You can use join to bound that tables results into one.
Inner join will return only results which matches on both sides.
So You can use it to match them in both tables by it and then filter for Your wanted value by classic where:
select * from task t inner join project p on t.dueDate = p.dueDate and t.completed = p.completed
where t.dueDate = '2012-08-17' and t.completed = 0

Try this instead:
SELECT dueDate, completed
FROM task AS t
WHERE (dueDate = "2012-08-17" AND completed = 0)
UNION ALL
SELECT dueDate, completed
FROM project AS p
WHERE (dueDate = "2012-08-17" AND completed = 0)
This should give you all records from each table where dueDate = "2012-08-17" and completed = 0.

Related

Fetch data based on another MySQL query

I have the following two queries. The first query is fetching a key called srNumber from first table called tags and then the second query is using that srNumber to fetch details from a second table called nexttable.
$tagQuery = "SELECT * FROM tags WHERE status = 0 AND currentStage = '1' AND assignedTo = '1' ORDER BY
deliveryDate ASC";
$tagQueryExecute = mysqli_query($conn, $tagQuery);
while($rows = mysqli_fetch_array($tagQueryExecute)){
$srNumber = $rows['srNumber'];
$nextQuery = "SELECT * FROM nexttable WHERE srNumber='$srNumber'";
$nextQueryExecute = mysqli_query($conn, $nextQuery);
$detailsFromNextTable = mysqli_fetch_array($nextQueryExecute);
//Show these details
}
For a small result this is not a big issue. But if the first query got so many results, then second query has to run as many times as number of loop. Is there any other way to do this efficiently?
NB: Please ignore the SQL injection issues with these queries. I just simplified it to show the problem
As you appear to have only 1 row in the second table, you would be better off with a join, MySQL: Quick breakdown of the types of joins gives some more info on the types of joins.
SELECT *
FROM tags t
JOIN nexttable n on t.srNumber = n.srNumber
WHERE t.status = 0 AND t.currentStage = '1' AND t.assignedTo = '1'
ORDER BY t.deliveryDate ASC
This also removes the SQL injection as well.
I would also recommend removing the * and just list the columns you intend to use, this also helps if you have columns with the same names in the different tables as you can add an alias to the specific columns.
FYI - the original problem you have is similar to What is the "N+1 selects problem" in ORM (Object-Relational Mapping)?

My SQL query is returning duplicate results

I have an annoying problem that I cannot get past.
I have a photograph database broken up into two tables:
Table 1 (snaps) consists of four columns:
'photoid', 'filename', 'location', 'created'
Table 2 (befter) which displays the photos in pairs consists of
'ppairid', 'beforeid', 'afterid', 'description'
Table 2 displays the photo pairs, as beforeid and afterid use the unique photoid INT from table 1. All quite simple it would seem.
BUT one of the queries that I have come up with (by location) duplicates if the location of beforeid and afterid are the same. So for example:-
SELECT *
FROM befter, snaps
WHERE (snaps.photoid = befter.beforeid OR snaps.photoid = befter.afterid)
AND snaps.location = 'oxford'
is fine when the photo location is different but not if they're the same. I've tried adding DISTINCT etc but I can't figure it out.
Any ideas please?
P
Use two left joins:
select b.*, sb.*, sa.*
from befter b left join
snaps sb
on sb.photoid = b.beforeid and sb.location = 'oxford' left join
snaps sa
on sa.photoid = b.afterid and sa.location = 'oxford'
where sb.photoid is not null or sa.photoid is not null;
This includes both the before and after snaps in the result set.
You have to tell the SQL statement what you are fetching from the given tables. If you take an * on two tables, then you will get duplicate results.
Try this:
SELECT
snaps.photoid, snaps.filename, snaps.location, snaps.created, befter.ppairid,
befter.beforeid, befter.afterid, befter.description
FROM
befter
INNER JOIN
snaps
ON
snaps.photoid = befter.ppairid
WHERE
snaps.location='oxford'
AND
(snaps.photoid = befter.beforeid OR snaps.photoid = befter.afterid)

Perform query on existing SQL result? Find result from subset of SQL result

I have a script that goes through all order history. It takes several minutes to print the results, but I noticed I perform several SQL statements that are similar enough I wonder if you could do another query on an existing SQL result.
For example:
-- first SQL request
SELECT * FROM orders
WHERE status = 'shipped'
Then, in a foreach loop, I want to find information from this result. My naive approach is to perform these three queries. Note the similarity to the query above.
-- grabs customer's LTD sales
SELECT SUM(total) FROM orders
WHERE user = :user
AND status = 'shipped'
-- grabs number of orders customer has made
SELECT COUNT(*) FROM orders
WHERE user = :user
AND status = 'shipped'
AND total != 0
-- grabs number of giveaways user has won
SELECT COUNT(*) FROM orders
WHERE user = :user
AND status = 'shipped'
AND total = 0
I end up querying the same table several times when the results I seek are subsets of the first query. I'd like to get information from the first query without performing more SQL calls. Some pseudocode:
$stmt1 = $db->prepare("
SELECT * FROM orders
WHERE status = 'shipped'
");
$stmt1->execute();
foreach($stmt1 as $var) {
$username = $var['username'];
$stmt2 = $stmt1->workOn("
SELECT SUM(total) FROM this
WHERE user = :user
");
$stmt2->execute(array(
':user' => $username
));
$lifesales = $stmt2->fetchColumn();
$stmt3 = $stmt1->workOn("
SELECT COUNT(*) FROM this
WHERE user = :user
AND total != 0
");
$stmt3->execute(array(
':user' => $username
));
$totalorders = $stmt3->fetchColumn();
$stmt4 = $stmt1->workOn("
SELECT COUNT(*) FROM this
WHERE user = :user
AND total = 0
");
$stmt4->execute(array(
':user' => $username
));
$totalgaws = $stmt4->fetchColumn();
echo "Username: ".$username;
echo "<br/>Lifetime Sales: ".$lifesales;
echo "<br/>Total Orders: ".$totalorders;
echo "<br/>Total Giveaways: ".$totalgaws;
echo "<br/><br/>";
}
Is something like this possible? Is it faster? My existing method is slow and ugly, I'd like a quicker way to do this.
We could do one pass through the table to get all three aggregates for all users:
SELECT s.user
, SUM(s.total) AS `ltd_sales`
, SUM(s.total <> 0) AS `cnt_prior_sales`
, SUM(s.total = 0) AS `cnt_giveaways`
FROM orders s
WHERE s.status = 'shipped'
GROUP
BY s.user
That's going to be expensive on large sets. But if we are needing that for all orders, for all users, that's likely going to be faster than doing separate correlated subqueries.
An index with leading column of user is going to allow MySQL to use the index for the GROUP BY operation. Including the status and total columns in the index will allow the query to be satisfied entirely from the index. (With the equality predicate on status column, we could also try an index with status as the leading column, followed by user column, then followed by total.
If we only need this result for a small subset of users e.g. we are fetching only the first 10 rows from the first query, then running a separate query is likely going to be faster. We'd just incorporate the condition WHERE s.user = :user into the query, as in the original code. But run just the one query rather than three separate queries.
We can combine that with the first query by making it into an inline view, wrapping it in parens and putting into the FROM clause as a row source
SELECT o.*
, t.ltd_sales
, t.cnt_prior_sale
, t.cnt_giveaways
FROM orders o
JOIN (
SELECT s.user
, SUM(s.total) AS `ltd_sales`
, SUM(s.total <> 0) AS `cnt_prior_sales`
, SUM(s.total = 0) AS `cnt_giveaways`
FROM orders s
WHERE s.status = 'shipped'
GROUP
BY s.user
) t
ON t.user = o.user
WHERE o.status = 'shipped'
I'm not sure about that column named "prior" sales... this is returning all shipped orders, without regard to comparing any dates (order date, fulfillment date, shipment date), which we would typically associate with a concept of what "prior" means.
FOLLOWUP
noticing that the question is modified, removing the condition "status = 'shipped'" from the count of all orders by the user...
I will note that we can move conditions from the WHERE clause into the conditional aggregates.
Not that all these results are needed by OP, but as a demonstration...
SELECT s.user
, SUM(IF(s.status='shipped',s.total,0)) AS `ltd_sales_shipped`
, SUM(IF(s.status<>'shipped',s.total,0)) AS `ltd_sales_not_shipped`
, SUM(s.status='shipped' AND s.total <> 0) AS `cnt_shipped_orders`
, SUM(s.status='canceled') AS `cnt_canceled`
, SUM(s.status='shipped' AND s.total = 0) AS `cnt_shipped_giveaways`
FROM orders s
GROUP
BY s.user
Once the results are returned from the database, you can not run an SQL on top of them. However you can store them in a temporary table, to reuse them.
https://dev.mysql.com/doc/refman/8.0/en/create-temporary-table.html
https://dev.mysql.com/doc/refman/8.0/en/create-table-select.html
https://dev.mysql.com/doc/refman/8.0/en/insert-select.html
You need to create a temporary table, and insert all the data from the select statement, and then you can run queries on that table. Not sure if it would help much in your case.
For your particular case you can do something like:
select user, (total = 0) as is_total_zero, count(*), sum(total)
from orders
where status = 'shipped'
group by user, total = 0
However you would have to do some additional summing to get the results of the second query which gives you the sums per user, as they would be divided into two different groups with a different is_total_zero value.

Need to run 3 queries that use the result of the previous query.. on PHP

I am creating a website that will show specific records to some folks in the company here. I am using PHP, HTML and MYSLQ to create it.
This is the basic layout I am trying to present:
It is an html website, and each row has collapsible rows on 2 levels:
Reference Number > Entries > Invoices
So in this logic, a Reference Number can have one or multiple Entries and an Entry can have one or multiple Invoices.
The html table is a no brainer, the problem is to feed the data into the website.
How I made it work...
Using PHP I created a procedure that first queries the database for the References, then I do a while loop to go through the References and then using the reference number I query the Entries and do a while loop to go through them and use the Entry Number to query for the Invoices.
Now, I am an amateur coder, but I do know that putting the query inside a while loop is extremely bad practice. So I am struggling to making this work properly.
Furthermore, I want this to work by using stored procedures so that all the querying happens on mysql server, rather than on the client side.
Here are the queries:
Query 1 (Main level):
SELECT
oc.sNumCajaTrailer AS Caja,
oc.dFechaCruce AS FechaSalida,
t.sNombre AS Transportista,
tfc.sCveTrafico AS Reference <--- This is the key for the next query
FROM cb_detalle_orden_carga doc
JOIN cb_orden_carga oc ON doc.iConsecutivoOrdenCarga = oc.iConsecutivo
JOIN cu_transfer t ON oc.sCveTransfer = t.sCveTransfer
JOIN cb_trafico tfc ON doc.sCveTrafico = tfc.sCveTrafico
JOIN cu_cliente cte ON tfc.sCveCliente = cte.sCveCliente
WHERE
(oc.dFechaCruce IS NULL OR oc.dFechaCruce IN (fechaSalida))
AND tfc.sCveCliente = 'CLT_6840' AND (doc.sCveTrafico LIKE '%N16%' OR doc.sCveTrafico LIKE '%N17%')
GROUP BY doc.sCveTrafico
Query 2 (FirstSubLevel)
SELECT b.sCveEntradaBodega AS Entry, <----- This is the key for next query
cp.sNombreProveedor AS NombreProveedor,
b.dFechaIngreso AS FechaIngreso,
b.iCantidadBultos AS Bultos,
cb.sDescripcion AS TipoBultos
FROM cb_bulto b
JOIN cb_entrada_bodega eb ON b.sCveEntradaBodega = eb.sCveEntradaBodega
JOIN cu_cliente_proveedor cp ON eb.sCveProveedor = cp.sCveProveedor
AND cp.sCveCliente = eb.sCveCliente
JOIN cb_detalle_orden_carga doc ON b.iConsecutivo = doc.iConsecutivoBulto
JOIN cb_orden_carga oc ON doc.iConsecutivoOrdenCarga = oc.iConsecutivo
JOIN ct_bulto cb ON b.sCveBulto = cb.sCveBulto
WHERE b.sCveTrafico = Reference
Query 3 (Second and last sublevel)
SELECT
f.sNumero AS numFactura,
eb.sNumTalon AS numGuia,
pf.sCveClienteProveedorProducto AS numParte,
pf.iCantidadProducto AS cantidadProducto,
f.sNumeroPedido AS numOrden,
b.iCantidadBultos AS cantidadBultos,
tb.sDescripcion AS tipoBultos
FROM
cb_bulto b
JOIN cb_relacion_remesa_banco_facturas rbf ON b.iConsecutivo = rbf.iConsecutivoBulto
JOIN cb_factura f ON rbf.iFolio = f.iFolio
JOIN cb_entrada_bodega eb ON b.sCveEntradaBodega = eb.sCveEntradaBodega
JOIN cb_producto_factura pf ON f.iFolio = pf.iFolio
JOIN ct_bulto tb ON b.sCveBulto = tb.sCveBulto
WHERE
b.sCveEntradaBodega = Entry
Any ideas????!!!!

Returning data from multiple tables in one query even if secondary tables do not apply

I have an issue getting data from three tables, which I want to return using one query.
I've done this before using a query something like this one:
$query = mysql_query("SELECT
maintable.`id`,
maintable.`somedata`,
maintable.`subtable1_id`,
subtable1.`somedata`,
subtable1.`subtable2_id`,
subtable2.`somedata`
FROM
`maintable` maintable,
`subtable1` subtable1,
`subtable2` subtable2
WHERE
maintable.`somedata` = 'some_search' AND
subtable1.`id` = maintable.`subtable1_id` AND
subtable2.`id` = subtable1.`subtable2_id`
")or die(mysql_error());
The problem this time is that the extra details might not actually apply. I need to return all results that match some_search in maintable, even if there is no subtable1_id specified.
I need something that will go along the lines of
WHERE
maintable.`somedata` = 'some_search'
AND IF maintable.`subtable1_id` IS NOT NULL (
WHERE subtable1.`id` = maintable.`subtable1_id` AND
subtable2.`id` = subtable1.`subtable2_id`
)
As you will probably guess, I am not an advanced mysql user! Try as I might, I cannot get the syntax right, and I have had no luck searching for solutions on the web.
Any help much appreciated!
It seems like the basic distinction you're looking for is between an INNER JOIN and a LEFT JOIN in MySQL.
An INNER JOIN will require a reference between the two tables. There must be a match on both sides for the row to be returned.
A LEFT JOIN will return matches in both rows, like an INNER, but it will also returns rows from your primary table even if no rows match in your secondary tables -- their fields will be NULL.
You can find example syntax in the docs.
If I got this right, you need to use MySQL LEFT JOIN. Try this:
SELECT
m.id,
m.somedata,
m.subtable1_id,
s1.somedata,
s1.subtable2_id,
s2.somedata
FROM
maintable m
LEFT JOIN
subtable1 s1
ON
m.subtable1_id = s1.id
LEFT JOIN
subtable2 s2
ON
s1.subtable2_id = s2.id
WHERE
m.somedata = 'some search'
This will return the data of maintable even if there's no equivalent data in subtable1 or 2 OR data of maintable and subtable1 if there's no equivalent data in subtable2.
How about this:
WHERE
maintable.`somedata` = 'some_search'
AND (maintable.`subtable1_id` IS NULL OR (
subtable1.`id` = maintable.`subtable1_id` AND
subtable2.`id` = subtable1.`subtable2_id` )
)
Keep in mind that this will result in a cross product of subtable1 and subtable2 with maintable when subtable1_id is NULL.

Categories