Unions in mysqli - php

I am attempting to get the latest result from two different tables (forum_posts and forum_replies)...I'm not sure of the best way of doing this, so I am attempting to use a UNION ALL to do this...
I did a test result to try to make sure the code worked; however, it doesn't appear to be working correctly. Even though there is data matching the requirements in the database, it is echoing out No Posts. So something about the query isn't processing correctly.
$latest = "(SELECT * FROM forum_posts WHERE post_subcat = '1' ORDER BY post_id) UNION ALL (SELECT * FROM forum_replies WHERE reply_subcat = '1' ORDER BY reply_id) LIMIT 1";
if(!$getlatest = $con->query($latest)){ echo 'No Posts'; }
if($getlatest = $con->query($latest)){ echo 'Post'; }
I'm new to unions, so I have a few questions.
1) I've seen a union work in mysql, but do they also work in mysqli?
2) Are there any restrictions to using unions (union/union all/ etc.)? Do columns have to be the same for comparison?
3) Did I do something wrong in my above code? I am probably overlooking something minor just from working too long, just not sure at this point.

You generally have to (overly flexible DBMS' notwithstanding) have the same column types and identifiers in the two queries you're unioning together and the order by applies to the final result set, not the interim one (although you can use sub-queries to order interim results if needed, not something that seems to be required in this specific case).
So something like this:
SELECT post_id as id,
post_date as dt
FROM forum_posts
WHERE post_subcat = '1'
UNION ALL
SELECT reply_id as id,
reply_date as dt
FROM forum_replies
WHERE reply_subcat = '1'
ORDER BY dt DESC
LIMIT 1
In terms of the mysql/mysqli distinction, that shouldn't matter, both methods end up doing the relevant work at the server side.
You usually use union all if you know there's no chance of duplicates so as to avoid any unnecessary sorting for the duplicate removal. Otherwise, use union if you do want duplicates removed.
As to whether you have a problem with your code, if the query function returns false, you should be checking the error functions for the specific failure reason. See mysqli_error for details.
And keep in mind it doesn't return false if there were no posts, it only returns false if there was an error. If the query worked and there were no posts, you'd end up with an empty result set.

1) I've seen a union work in mysql, but do they also work in mysqli?
It should work on mysqli also because mysqli is another extension to access Mysql 4.1 onwards
2) Are there any restrictions to using unions (union/union all/ etc.)? Do columns have to be the same for comparison?
Yes. All the columns from two tables needs to be of same data type and the no of columns also should match. I suspect this is your problem.
3) Did I do something wrong in my above code? I am probably overlooking something minor just from working too long, just not sure at this point.

If I've understood what you're after correctly, I believe the following will work:
SELECT * FROM forum_posts fp WHERE fp.post_subcat = (SELECT MAX(fp1.post_subcat) FROM forum_posts fp1)
UNION
SELECT * FROM forum_replies fr WHERE fr.reply_subcat = (SELECT MAX(fp.post_subcat) FROM forum_posts fp)

Did I do something wrong in my above code?
Sure.
First, you are running your query two times.
Second, you are checking wrong value to see if there were any posts. http://php.net/mysqli_query

Related

How to order the ORDER BY using the IN() mysql? [duplicate]

I am wondering if there is away (possibly a better way) to order by the order of the values in an IN() clause.
The problem is that I have 2 queries, one that gets all of the IDs and the second that retrieves all the information. The first creates the order of the IDs which I want the second to order by. The IDs are put in an IN() clause in the correct order.
So it'd be something like (extremely simplified):
SELECT id FROM table1 WHERE ... ORDER BY display_order, name
SELECT name, description, ... WHERE id IN ([id's from first])
The issue is that the second query does not return the results in the same order that the IDs are put into the IN() clause.
One solution I have found is to put all of the IDs into a temp table with an auto incrementing field which is then joined into the second query.
Is there a better option?
Note: As the first query is run "by the user" and the second is run in a background process, there is no way to combine the 2 into 1 query using sub queries.
I am using MySQL, but I'm thinking it might be useful to have it noted what options there are for other DBs as well.
Use MySQL's FIELD() function:
SELECT name, description, ...
FROM ...
WHERE id IN([ids, any order])
ORDER BY FIELD(id, [ids in order])
FIELD() will return the index of the first parameter that is equal to the first parameter (other than the first parameter itself).
FIELD('a', 'a', 'b', 'c')
will return 1
FIELD('a', 'c', 'b', 'a')
will return 3
This will do exactly what you want if you paste the ids into the IN() clause and the FIELD() function in the same order.
See following how to get sorted data.
SELECT ...
FROM ...
WHERE zip IN (91709,92886,92807,...,91356)
AND user.status=1
ORDER
BY provider.package_id DESC
, FIELD(zip,91709,92886,92807,...,91356)
LIMIT 10
Two solutions that spring to mind:
order by case id when 123 then 1 when 456 then 2 else null end asc
order by instr(','||id||',',',123,456,') asc
(instr() is from Oracle; maybe you have locate() or charindex() or something like that)
If you want to do arbitrary sorting on a query using values inputted by the query in MS SQL Server 2008+, it can be done by creating a table on the fly and doing a join like so (using nomenclature from OP).
SELECT table1.name, table1.description ...
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx)
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx
If you replace the VALUES statement with something else that does the same thing, but in ANSI SQL, then this should work on any SQL database.
Note:
The second column in the created table (orderTbl.orderIdx) is necessary when querying record sets larger than 100 or so. I originally didn't have an orderIdx column, but found that with result sets larger than 100 I had to explicitly sort by that column; in SQL Server Express 2014 anyways.
SELECT ORDER_NO, DELIVERY_ADDRESS
from IFSAPP.PURCHASE_ORDER_TAB
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126')
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)
worked really great
Ans to get sorted data.
SELECT ...
FROM ...
ORDER BY FIELD(user_id,5,3,2,...,50) LIMIT 10
The IN clause describes a set of values, and sets do not have order.
Your solution with a join and then ordering on the display_order column is the most nearly correct solution; anything else is probably a DBMS-specific hack (or is doing some stuff with the OLAP functions in standard SQL). Certainly, the join is the most nearly portable solution (though generating the data with the display_order values may be problematic). Note that you may need to select the ordering columns; that used to be a requirement in standard SQL, though I believe it was relaxed as a rule a while ago (maybe as long ago as SQL-92).
Use MySQL FIND_IN_SET function:
SELECT *
FROM table_name
WHERE id IN (..,..,..,..)
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);
For Oracle, John's solution using instr() function works. Here's slightly different solution that worked -
SELECT id
FROM table1
WHERE id IN (1, 20, 45, 60)
ORDER BY instr('1, 20, 45, 60', id)
I just tried to do this is MS SQL Server where we do not have FIELD():
SELECT table1.id
...
INNER JOIN
(VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
) AS X(id,sortorder)
ON X.id = table1.id
ORDER BY X.sortorder
Note that I am allowing duplication too.
Give this a shot:
SELECT name, description, ...
WHERE id IN
(SELECT id FROM table1 WHERE...)
ORDER BY
(SELECT display_order FROM table1 WHERE...),
(SELECT name FROM table1 WHERE...)
The WHEREs will probably take a little tweaking to get the correlated subqueries working properly, but the basic principle should be sound.
My first thought was to write a single query, but you said that was not possible because one is run by the user and the other is run in the background. How are you storing the list of ids to pass from the user to the background process? Why not put them in a temporary table with a column to signify the order.
So how about this:
The user interface bit runs and inserts values into a new table you create. It would insert the id, position and some sort of job number identifier)
The job number is passed to the background process (instead of all the ids)
The background process does a select from the table in step 1 and you join in to get the other information that you require. It uses the job number in the WHERE clause and orders by the position column.
The background process, when finished, deletes from the table based on the job identifier.
I think you should manage to store your data in a way that you will simply do a join and it will be perfect, so no hacks and complicated things going on.
I have for instance a "Recently played" list of track ids, on SQLite i simply do:
SELECT * FROM recently NATURAL JOIN tracks;

Database Group By error

I've been working with Mysql for a while, but this is the first time I've encountered this problem.
The thing is that I have a select query...
SELECT
transactions.inventoryid,
inventoryName,
inventoryBarcode,
inventoryControlNumber,
users.nombre,
users.apellido,
transactionid,
transactionNumber,
originalQTY,
updateQTY,
finalQTY,
transactionDate,
transactionState,
transactions.observaciones
FROM
transactions
LEFT JOIN
inventory ON inventory.inventoryid = transactions.inventoryid
LEFT JOIN
users ON transactions.userid = users.userid
GROUP BY
transactions.transactionNumber
ORDER BY
transactions.inventoryid
But the GROUP BY is eliminating 2 values from the QUERY.
In this case, when I output:
foreach($inventory->inventory as $values){
$transactionid[] = $values['inventoryid'];
}
It returns:
2,3,5
If I eliminate the GROUP BY Statement it returns
2,3,4,5,6
Which is the output I need for this particular case.
The question is:
Is there a reason for this to happen?
If I'm grouping by a transaction and that was supposed to affect the query, wouldn't it then return only 1 value?
Maybe I'm over thinking this one or been working too long on the code that I don't see the obvious flaw in my logic. But if someone can lend me a hand I would appreciate it.
In standard SQL you can only SELECT colums which are
contained in GROUP BY clause
(or) aggregate "colums", like MAX() or COUNT().
You need to consult the MySQL description of the interpretation they use for columns which are not contained by GROUP BY (and which are no aggregated column) MySQL Handling of GROUP BY to find out what happens here.
Do you need more information?

if else clause in sql, automatic rebuild of indexes

I am in need of a sql construct which gives the following functionality.
select if-expression if status='regular' else else-expression
from table-name ;
This operation will be used very frequently. So, I am considering building an index for this operation.
But, I heard that indexes are not rebuilt after table is being updated. Is there a way we can have automatically rebuild indexes?
Thanks in advance
The translation in SQL of your statement is:
select (case when status = 'regular' then <if-expression> else <else-expression> end)
from tablename;
An index will not help with this query, because you are not limiting the rows in any way. An index can help when you have filters in a where clause, joins, and correlated subqueries (and sometimes I think with group by).
And as MarcB points out in a comment, MySQL (and all other databases) keep indexes up to date for insert, update, and delete operations.
You did not provide a more detailed example.
There are several alternative solutions to your idea, not just the "use-the-index-luke-solution". And depends a lot on your database.
Another alternative could be using "union". In some circumstances could use too much resources, in others, may be the optimal solution, even if using too many records.
SELECT
<if-expression>
FROM
MyTable
WHERE
(MyTable.status = 'regular')
UNION
SELECT
<else-expression>
FROM
MyTable
WHERE
(MyTable.status <> 'regular')
And, in some circumstances, you may also add
SELECT
<if-expression>
FROM
MyTable
WHERE
(MyTable.status = 'regular')
ORDER BY <indexed-fields-used-in-expression>
UNION
SELECT
<else-expression>
FROM
MyTable
WHERE
(MyTable.status <> 'regular')
ORDER BY <indexed-fields-used-in-expression>
Cheers.

How to optimize this query which taking long time.?

While working with following query on mysql, Its taking to execute around 10sec.
SELECT SQL_CALC_FOUND_ROWS DISTINCT
b.appearance_id,
b.photo_album_id,
b.eventcmmnt_id,
b.id,
b.mem_id,
b.subj,
b.body,
b.image_link as photo_image_uploaded,
b.bottle_id,
b.date,
b.parentid,
b.from_id,
b.visible_to,
pa.photo_big as image_link,
b.post_via,
b.youtubeLink,
b.link_image,
b.link_url,
b.auto_genrate_text,
badges.badge_img,
badges.badge_bottle_img,
b.type,b.share_url_title
FROM bulletin b
INNER JOIN network n
ON (n.mem_id = b.mem_id)
LEFT JOIN badges
ON (b.bottle_id=badges.badge_id)
LEFT JOIN photo_album as pa
ON (pa.photo_id=b.photo_album_id)
JOIN members mem
ON (b.mem_id=mem.mem_id and mem.deleted<>'Y')
WHERE b.parentid = '0'
AND ('$userid' IN (n.frd_id, b.mem_id,b.from_id))
GROUP BY b.id
ORDER BY b.id DESC
LIMIT 0,10
The Inner query inside IN constraint has many frd_id,mem_id,from_id So I think because of that above query is executing slowly... So please help to optimize above query
Thanks
Check, that you have indexes on fields, which are used for joins.
As you have already been hinted, post the EXPLAIN SELECT... plan for the query. Why guess when you can check? (And a SQL Fiddle would be great too).
That will tell whether you need indexes (very, very likely!) and which indexes and how structured (do not go and index everything, since this may even harm performances!).
One suggestion I can already give: if you have an index field on members of the form
(mem_id, deleted, ...)
as you should, verify that deleted is indeed a boolean (if it is not, convert it to an enum of 'Y' and 'N'), and specify the condition like this:
on(b.mem_id=mem.mem_id AND NOT mem.deleted)
since this will go much easier on the indexing. If the field is not boolean, for example it may have the values 'Y', 'N' and 'P' for 'Pending', and you used a varchar field, then the <> has a considerable performance impact on the index - I'd even go out on a limb and say that the index is effectively neutralized. Indexes work best with matches, not with non-matches as <>.
I suspect you can also re-engineer this expression
AND ('$userid' IN (n.frd_id, b.mem_id,b.from_id))
by moving it, albeit partially, in the b JOIN. Or you could consider, depending on your tables' cardinality, using a UNION instead.

This query on mysql is taking forever to execute

im making a simple admin module to query the database to show the results. Im using this query via php:
SELECT
*
FROM myTable
WHERE id in(SELECT
id_registro
FROM myOtherTable
where id_forma='".$id_club."' and fecha_visita Like '%".$hoy."%'
)
order by id DESC
The result shows, however, it takes very long like 2 minutes..Anyone can help me out?
Thanks!
Without seeing your database, it is hard to find a way to make it faster.
Maybe you can try to turn your WHERE IN to INNER JOIN. To something like this
SELECT * FROM myTable INNER JOIN myOtherTable
ON (myTable.id = myOtherTable.id_registro)
WHERE myOtherTable.id_forma = '$id_club'
AND myOtherTable.fecha_visita LIKE '%$hoy%'
ORDER BY myTable.id DESC
Noted that you should sanitize your variable before putting it SQL query or using PDO prepare statement.
Sub Queries takes always time, so its better to ignore them as much as possible.
Try to optimize your query by checking its cardinality,possible keys getting implemented by DESC or EXPLAIN , and if necessary use FORCE INDEX over possible keys.
and I guess you can modify your query as:
SELECT
*
FROM myTable
inner join id_registro
on (id = id_forma )
where
id_forma='".$id_club."' and fecha_visita Like '%".$hoy."%'
order by id DESC
LIKE in mysql may take a long time ,with or without index.
Do u have a very large DB?

Categories