if else clause in sql, automatic rebuild of indexes - php

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.

Related

What does "s." and "n" mean/do in this mysql statement?

I'm currently working on a project that requires me look at someone elses code, i'm still new to some of this stuff and there is something that I dont quite understand so was hoping someone could shed some light on what the code is actually doing or what it means?
Here is the statement in question:
select s.* from $tableA n, $tableB s where
n.id='$send' and
n.status='$status' and
n.field=s.id";
I understand that down to basics this statement is getting all of the fields from tableA and tableB im just unsure what the s. does or what the n does in this statement? are they simply there as identifiers or am I completely wrong in this manner? I am happy to provide more information if it is necessary.
They are called SQL Table Aliases and are basically temporary names which you give to the tables in order to have better readability when you use the table names to specify a column.
In your example
SELECT s.* FROM $tableA n, $tableB s
WHERE n.id='$send'
AND n.status='$status'
AND n.field=s.id ;
is the same as
SELECT $tableB.* FROM $tableA, $tableB
WHERE $tableA.id='$send'
AND $tableA.status = '$status'
AND $tableA.field = $tableB.id ;
but obviously it's easier to read.
The table aliases are even more useful when you join more tables and are absolutely a must when you make self joins.
Syntax note:
You may or you may not use the AS keyword when alias a table.
SELECT table_name AS alias
is the same as
SELECT table_name alias
and although it's longer sometimes it leads to a better readability (for example in a large and messy query the big AS is easier to spot :)

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.

Unions in mysqli

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

Is it better do to a union in SQL or separate queries and then use php array_merge?

I have a SQL query that has 4 UNIONS and 4 LEFT JOINS. It is layed out as such:
SELECT ... FROM table1
LEFT JOIN other_table1
UNION SELECT ... FROM table2
LEFT JOIN other_table2
UNION SELECT ... other_table3
LEFT JOIN other_table3
UNION SELECT ... FROM table4
LEFT JOIN other_table4
Would it be better to run 4 separate queries and then merge the results with php after the fact? Or should I keep them together? Which would provide that fastest execution?
The most definitive answer is to test each method, however the UNION is most likely to be faster as only one query is run by MySQL as opposed to 4 for each part of the union.
You also remove the overhead of reading the data into memory in PHP and concatenating it. Instead, you can just do a while() or foreach() or whatever on one result.
In this case, it depends on the number of records you are going to get out of the result. Since you are using left join in all unions, I suggest to do different fetch to avoid bottleneck in SQL and merge the results in PHP
When a query is executed from a programming language, following steps occur
A connection is created to between application and database (or an existing connection is used from pool)
Query is sent to database
Database sends the result back
Connection is released to pool
If you are running N number of queries, above steps happen N number of times, which you can guess will definitely slow down the process. So ideally we should keep number of queries to as minimum as possible.
It will make sense to break a query into multiple parts if single query becomes complex and it gets difficult to maintain and takes a lot of time to execute. In that case too, good way will be to optimize the query itself.
As in your case, query is pretty simple, and as someone has pointed out that union will also help removing duplicate rows, the best way is to go for sql query than php code. Try optimization techniques like creating proper indexes on tables.
The UNION clause can be faster, because it will return distinct records at once (duplicated records won't be returned), otherwise you will need to do it in the application. Also, in this case it may help to reduce a traffic.
From the documentation:
The default behavior for UNION is that duplicate rows are removed from
the result. The optional DISTINCT keyword has no effect other than the
default because it also specifies duplicate-row removal. With the
optional ALL keyword, duplicate-row removal does not occur and the
result includes all matching rows from all the SELECT statements.
You can mix UNION ALL and UNION DISTINCT in the same query. Mixed UNION
types are treated such that a DISTINCT union overrides any ALL union
to its left. A DISTINCT union can be produced explicitly by using
UNION DISTINCT or implicitly by using UNION with no following DISTINCT
or ALL keyword.

Propel equivalent of "exists"

I am new to Propel and have been reading the documentation. But, I have not found a clear equivalent to the EXISTS and NOT EXISTS constructs from SQL. Linq in .NET, for instance, has Any(). Is there an equivalent to the following in "idiomatic" Propel?
SELECT a.column1, a column2, a.etc
FROM TableA a
WHERE NOT EXISTS (SELECT 1
FROM TableB b
WHERE b.someIdColumn = a.someIdColumn
AND b.aNullableDateColumn IS NULL)
After doing some more digging, I believe I have an answer to my question, or at least as good an answer as is currently available.
What comes after EXISTS or NOT EXISTS is a subquery. While that fact seems obvious, it did not originally occur to me to focus my search for help on subqueries. I found a few resources on the topic. Essentially, the options are to rewrite the query using JOINs (as is the heart of the answer by #Kaltas) or to use Criteria::CUSTOM. I decided I would likely prefer the second option, since it allows me to keep the subquery, potentially helping my database performance.
I did a lot of reading, then, about Criteria::CUSTOM, but the only reading that really helped me was reading the Propel 1.5 source. It's very simple, really. Just put the subquery, verbatim (using the database's table and column names, not Propel's object names) along with EXISTS or NOT EXISTS in the where call, like:
TableAQuery::create()
->where('NOT EXISTS (SELECT 1 FROM TableB WHERE TableA.someIdColumn = TableB.someIdColumn AND TableB.aNullableDateColumn IS NULL)')
->find();
It's that simple. Internally, the where method goes through a few possibilities for interpreting the clause, and finding no matches, it treats the clause as being of Criteria::CUSTOM and inserts it into the SQL query as-is. So, I could not use table aliases, for example.
If I ever have time, maybe I'll work on a more "ORM-ish" way to do this and submit a patch. Someone will probably beat me to it, though.
As in propel 1.6 u now can use Criteria::IN and Criteria::NOT_IN
Example : Select all users that are not in an UserGroup
$users = UserQuery::create()->filterById(UserPerUserGroupQuery::create()->select('user_id')->find(), CRITERIA::NOT_IN)
->orderByUserName()
->find();
I think you could rewrite the query as:
SELECT
a.column1,
a.column2,
a.etc
FROM
TableA a
WHERE
(SELECT
COUNT(*)
FROM
TableB b
WHERE
b.someIdColumn = a.someIdColumn
AND
b.aNullableDateColumn IS NULL
) > 0
which is easily doable in Propel.
Or even cleaner and easier to accomplish in Propel:
SELECT
a.column1,
a.column2,
a.etc
FROM
TableA a
LEFT JOIN
TableB b ON (b.someIdColumn = a.someIdColumn)
WHERE
b.aNullableDateColumn IS NULL
AND
b.primaryKeyColumn IS NOT NULL
Propel 2 can do:
TableAQuery::create()
->useTableBNotExistsQuery()
->filterByNullableDateColumn(null)
->endUse()
->find();
or
$nestedB = TableBQuery::create()
->filterByNullableDateColumn(null)
->where('TableB.someIdColumn = TableA.someIdColumn');
TableAQuery::create()->whereExists(nestedB)->find();

Categories