See operator documentation for info on all operators. @ is the error suppression operator, which means that if an error/notice is generated, it will be thrown away without you ever knowing about it. If you need to use it, you are usually doing something wrong.
You are using the mysql extension, but should look into mysqli (i = improved). The resource link is what mysql_connect / mysqli::__construct returns, as shown by the first word on this line (return value)
resource mysql_connect ([ string $server = ini_get("mysql.default_host") [, string $username = ini_get("mysql.default_user") [, string $password = ini_get("mysql.default_password") [, bool $new_link = false [, int $client_flags = 0 ]]]]] )
Further down on that page it says
Returns a MySQL link identifier on success or FALSE on failure.
So, calling this function either gives you a link resource on success, or boolean false on failure, which you can follow up with a call to mysql_error() as shown in the first example on that page.
A call to mysql_query also returns a resource, a result set resource instead of a link resource, which gives you access to the data returned from your query. It can also return true on success (for other types of queries) and false on failure for any type of query.
Due to the possibility of it returning false, you can't pass the result of mysql_query directly to mysql_fetch_array / mysql_fetch_assoc without risking errors. Once again, have a look at the documentation. Just like with mysql_conncet, the first example shows you how to check for errors.
Now, the result set resource has an internal row pointer. Each time you call one of the mysql_fetch_... functions, this row pointer is advanced to the next row. So
/* if $result is a result set resource, it always starts off at the first row
* Same as if you called mysql_data_seek($result, 0)
$result = mysql_query(...);
if (!$result)
exit; # you should deal with errors in a better way than this
/* automatically advances the internal row pointer for $result on each call to mysql_fetch_assoc
* thus letting you step through the entire result set, row by row.
* once you get "beyond the last row", false is returned and the loop exits */
while ($row = mysql_fetch_assoc($result)) {
}
However
while ($row = mysql_fetch_assoc(mysql_query(...)))
means that on each loop, execute the query, which returns a new result set, which means that the internal row pointer is always at the first row. This new result set is then used for the call to mysql_fetch_assoc(), which means that $row will contain the exact same data every time, and that the loop goes on forever.