I apply the same 2 queries to 3 tables, so just loop round and substitute the table names. The first INSERT query always works on test servers and on production. The second DELETE query works on two different test servers but is failing on production and throwing mysql error 1046 No database selected.
Affected code extract:
$queries = array(
"INSERT INTO knd_bkg.#table#
SELECT DISTINCT SQL_NO_CACHE
B.ASIN, Author, Title, EditorialReview, DetailPageURL, current_price, salesrank, number_reviews, erotica, G.BrowseNodeId, price_salesrank_checked_on
FROM bkd_books.club_books B
USE INDEX (free_quality)
RIGHT JOIN bkd_books.book_genre G on G.ASIN=B.ASIN
WHERE EXISTS
(
SELECT 1 FROM knd_bkg.genres K WHERE K.browsenode=G.BrowseNodeId
)
AND #priceWhere#
AND price_salesrank_checked_on > '#newerThan#'
AND B.number_reviews >= 4
AND B.rating >= 4.0
AND B.language = 'english'
AND B.hide_knd=0
AND B.public_domain=0;",
"DELETE T FROM knd_bkg.#table# T
INNER JOIN
(
SELECT N.* FROM knd_bkg.#table# N
INNER JOIN
(
SELECT DISTINCT ASIN, BrowseNodeId FROM knd_bkg.#table#
INNER JOIN knd_bkg.genres ON knd_bkg.genres.browsenode=knd_bkg.#table#.BrowseNodeId
WHERE isFiction=1
) F
ON F.ASIN=N.ASIN
INNER JOIN knd_bkg.genres
ON knd_bkg.genres.browsenode=N.BrowseNodeId
WHERE knd_bkg.genres.isNonFiction=1
) D
USING (ASIN, BrowseNodeId)
WHERE D.ASIN=T.ASIN AND D.BrowseNodeId=T.BrowseNodeId"
);
$tables = array(
"free_books" => array('priceWhere' => "B.current_price = 0", 'timePeriod' => $freeTimePeriod),
"99_books" => array('priceWhere' => "B.current_price > 0 AND B.current_price < 100", 'timePeriod' => $paidTimePeriod),
"399_books" => array('priceWhere' => "B.current_price >= 100 AND B.current_price < 400", 'timePeriod' => $paidTimePeriod),
);
connectSlaveDB();
foreach ($tables as $table => $data) {
$newerThan = date("Y-m-d H:i:s", strtotime("-" . $data['timePeriod'] . " hours"));
foreach ($queries as $query) {
$query = str_replace('#table#', $table, $query);
$query = str_replace('#newerThan#', $newerThan, $query);
$query = str_replace('#priceWhere#', $data['priceWhere'], $query);
Logger::_write(LOG_VERBOSE, "API - api/bkg/rebuild query: $query");
$result = mysql_query($query);
if (!$result) {
Logger::_write(LOG_ERR, "API - api/bkg/rebuild FAILED: " . mysql_error());
$success = false;
} else {
Logger::_write(LOG_VERBOSE, "API - api/bkg/rebuild SUCCESS");
}
}
}
And connectSlaveDB()
function connectSlaveDB()
{
global $dbSlave, $dbUser, $dbPass, $dbName;
$connSlave = mysql_connect($dbSlave, $dbUser, $dbPass, TRUE);
if (!$connSlave) {
Logger::_write(LOG_CRIT, "Unable to connect to slave DB");
include 'screens/error.inc.php';
exit();
}
mysql_set_charset('utf8', $connSlave);
mysql_select_db($dbName, $connSlave);
return TRUE;
}
The tables are MEMORY tables, but I get the same error with MYISAM, and only on production. This query is running on a replication slave and does not involve the master, but previous code TRUNCATES the tables so I don't believe it is down to permissions. When the query is taken out of the log file, it works fine in phpMyAdmin. And it's fine on the test servers.
I don't get the reason for the 1046 No database selected error. And after 3 hours I might be missing something really obvious. Especially as the failure pattern is like this, for one DB connection:
INSERT on free_books - success
DELETE on free_books - fail
INSERT on 99_books - success
DELETE on 99_books - fail
INSERT on 399_books - success
DELETE on 399_books - fail
Please excuse ugly globals and deprecated mysql* functions. This is a legacy app that is rather out of date. I know...
The syntax of your DELETE query may be causing the problem
DELETE T FROM
I know this has caused issues with UPDATE queries sometimes before due to a mysql bug.
Re-writing your query to avoid the T reference should resolve this issue.
Looks like the T alias in the DELETE was causing problems, but I'm not quiet sure why. The problem is now fixed by making the DELETE query to be:
"DELETE knd_bkg.#table# FROM knd_bkg.#table#
INNER JOIN
(
SELECT N.* FROM knd_bkg.#table# N
INNER JOIN
(
SELECT DISTINCT ASIN, BrowseNodeId FROM knd_bkg.#table#
INNER JOIN knd_bkg.genres ON knd_bkg.genres.browsenode=knd_bkg.#table#.BrowseNodeId
WHERE isFiction=1
) F
ON F.ASIN=N.ASIN
INNER JOIN knd_bkg.genres
ON knd_bkg.genres.browsenode=N.BrowseNodeId
WHERE knd_bkg.genres.isNonFiction=1
) D
USING (ASIN, BrowseNodeId)
WHERE D.ASIN=knd_bkg.#table#.ASIN AND D.BrowseNodeId=knd_bkg.#table#.BrowseNodeId"
Related
I tried to execute the query, but the result was null, I executed the same query in PhpMyAdmin and returns the result.
The problem is when the query contains SELECT inside another SELECT statement (imbricate), so when the query contains only one SELECT statement, it works fine and returns the result. Here is the code:
db_config.php
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "university_db";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
get_planning.php
<?php
require 'db_config.php';
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT cours.jour,
TIME_FORMAT(cours.heure_debut, '%H:%i') AS debut,
TIME_FORMAT(cours.heure_fin, '%H:%i') AS fin,
enseignant.nom_ens as prof, modules.nom_mod as module, salles.nom_salle as salle
FROM cours, promotion, enseignant, modules, salles
WHERE cours.id_promo = (SELECT id_promo FROM promotion WHERE promotion.niveau = '2'
AND promotion.id_speci = (SELECT id_speci FROM spécialité WHERE nom_speci = 'MGL'))
AND cours.id_promo = promotion.id_promo AND cours.id_ens = enseignant.id_ens AND cours.id_salle = salles.id_salle AND cours.id_mod = modules.id_mod
ORDER BY cours.id_cours;";
$result = $conn->query($sql);
if($result == null)
echo "Result is empty!";
Output:
Result is empty!
Information:
PHP: Version 7.3.5
Database: MySQL
Three suggestions for troubleshooting this problem:
Change your cours.id_promo = (SELECT ... to cours.id_promo IN (SELECT... and do the same to the line after it. Why? if you use = and the inner select statement returns more than one result, boom. Query fails. SQL is, at its heart, a set-processing language, and this IN that checks that this is a member of the set that.
echo your $sql value to make sure the statement in it is correct. Try running the exact statement via phpmyadmin to make sure it gives you what you expect.
You have this
if($result == null)
echo "Result is empty!";
Change it to this
if(!$result)
echo 'sql failure:', $conn->error, ': ', $sql;
The query() method only returns a falsey result if the query failed. If it succeeds but finds no matching rows, query() returns an empty result set. So anytime your $result is falsey, you made a programming error in your query. The echo I mentioned will diagnose it for you.
Pro tip Always check SQL operations for errors.
Pro tip 2. Bring your SQL skills into the 21st century. Use explicit join operations rather than old-timey comma-join operations. Change ...
SELECT cours.jour, whatever, whatever
FROM cours
JOIN promotion ON cours.id_promo = promotion.id_promo
JOIN enseignant ON cours.id_ens = enseignant.id_en
JOIN modules ON cours.id_mod = modules.id_mod
JOIN salles ON cours.id_salle = salles.id_salle
WHERE cours.id_promo IN (SELECT id_promo FROM promotion WHERE promotion.niveau = '2')
AND promotion.id_speci IN (SELECT id_speci FROM spécialité WHERE nom_speci = 'MGL')
ORDER BY cours.id_cours
The relationships between your tables are much easier to read and understand this way. And, you can change JOIN to LEFT JOIN if your application requires it.
Just a suggestion.
Instead of nested old implicit join syntax based list table, where clause and nested sub-query should use explicit join syntax.
SELECT cours.jour
, TIME_FORMAT(cours.heure_debut, '%H:%i') AS debut
, TIME_FORMAT(cours.heure_fin, '%H:%i') AS fin
, enseignant.nom_ens as prof
, modules.nom_mod as module
, salles.nom_salle as salle
FROM cours
INNER JOIN promotion ON cours.id_promo = promotion.id_promo
AND promotion.niveau = '2'
INNER JOIN enseignant ON cours.id_ens = enseignant.id_ens
INNER JOIN modules cours.id_mod = modules.id_mod
INNER JOIN salles ON cours.id_salle = salles.id_salle
ORDER BY cours.id_cours;
I've the following query executed via PHP, it used to work like yesterday, and stopped working today, here's it :
SELECT
*,
(SELECT IF (pro.uid= '4342','True','False') FROM pro) AS 'uid'
FROM
Episodes, pro
ORDER BY
Episodes.id DESC
LIMIT 9
The '4342' is changed with GET param, I'm having the following error :
Commands out of sync; you can't run this command now
My PHP API is as the following ( snippets ) :
<?php
include_once "db_config.php";
DEFINE ('DB_HOST', $host);
DEFINE ('DB_USER', $user);
DEFINE ('DB_PASSWORD', $pass);
DEFINE ('DB_NAME', $database);
$mysqli = #($GLOBALS["___mysqli_ston"] = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD)) OR die ('Could not connect to MySQL');
#((bool)mysqli_query($GLOBALS["___mysqli_ston"], "USE " . constant('DB_NAME'))) OR die ('Could not select the database');
?>
<?php
if(isset($_GET['meaE'], $_GET['uid']))
{
$query="SELECT * , (SELECT IF (pro.uid= '".$_GET['uid']."','True','False') FROM pro ) AS 'uid' FROM Episodes,pro ORDER BY Episodes.id DESC LIMIT 9
";
$resouter = mysqli_query($GLOBALS["___mysqli_ston"], $query);
}
$set = array();
$total_records = mysqli_num_rows($resouter);
if($total_records >= 1){
while ($link = mysqli_fetch_array($resouter, MYSQLI_ASSOC)){
$set['APITest'][] = $link;
}
}
echo $val= str_replace('\\/', '/', json_encode($set));
?>
I want to have the following output :
TABLE A - TABLE B
ID NAME TEXT UID - Check_ID
The query I want :
Select * From Table A, Check if "134123123" is exists in B, if yes / no add a record for all the taken records from Table A with the value of checking (True/False) .
What's happening with #sagi query is :
There are 3 records in Table B, and 10 records in Table A, when the query is executed, the result is 30 records, each record in Table B has 10 records from Table A, and the "uid" record value is True where the uid is '134123123', and it's false where it's not '134123123' .
Your query seems wrong, I don't know how it worked yesterday, but right now your are cross joining with out a condition (could be correct but I doubt it) - that means each record in episodes will be joined to each record in pro . In addition, your subquery with the IF will return more then 1 record unless this table contains only 1 record (which again - I doubt it) .
Try this:
SELECT distinct t.*, IF (s.uid is null,'True','False') AS 'uid'
FROM Episodes t
Left JOIN pro s
ON(s.uid = '4342')
ORDER BY t.id DESC
LIMIT ?????
Try using a sub query instead of a join:
Select t.*,
Case when exists(select 1 from pro s where s.uid = '4342')
Then 'true' else 'false'
End as 'uid'
From episodes t
Order by t.id desc
Limit 9;
I have a query with multiple joins in it. After I take the results and run it through a Id-checker I want to be able to delete records from that array where the IDDestination equals $ID.
Since this query has joins on it and I am filtering them based on one of the joined tables, How do I go about deleting those records from the array based off that joined table?
And I only wanted this to happen after the user confirms.
$query = "
select d.IDCourse,
d.name as course_name,
d.slug,
d.short_description,
d.address,
e.city_name,
e.state_code,
d.zip,
e.city_slug,
e.state_slug,
h.IDDestination,
LOWER(e.iso_code) as country_slug, a.*,
b.IDTeetimeType,
c.name as teetime_type,
b.start_time,b.end_time,
(case dayofweek(a.teetime_dt)
when 1 then `b`.`sun`
when 2 then `b`.`mon`
when 3 then `b`.`tue`
when 4 then `b`.`wed`
when 5 then `b`.`thu`
when 6 then `b`.`fri`
when 7 then `b`.`sat`
end) AS `price`, g.tax_rate, f.alias
from cart_course_teetimes a
join course_priceplan b
on a.IDCoursePricePlan = b.IDCoursePricePlan
join course_teetime_type c
on b.IDTeetimeType = c.IDTeetimeType
join course d
on b.IDCourse = d.IDCourse
join vw_cities e
on d.IDCity = e.IDCity
join destinations_cities h
on h.IDCity= d.IDCity
LEFT JOIN (SELECT * FROM media_mapping WHERE is_main_item=1 AND IDGalleryType=3) f
ON d.IDGallery = f.IDGallery
left join course_tax
g on a.IDCourseTax = g.IDCourseTax
where a.IDCart = :cart_id
order by d.name, a.teetime_dt, b.start_time;";
$prepared = array(
"cart_id" => $idCart,
);
$conn = new DBConnection();
$results = $conn->fetch($query, $prepared);
$conn = null;
$results = !empty($results) ? $results : array();
$id = null;
foreach($results as $row) {
// Set ID for the first record.
if($id === null)
$id = $row['IDDestination'];
// will stay true, otherwise it's false and we should kill the loop.
if($id != $row['IDDestination']) {
$newid=$row['IDDestination'];
echo "<script type='text/javascript'> emptycart();</script>";
$query = "DELETE FROM cart_course_teetimes a WHERE h.IDDestination='.$id.'";
$res =mysql_query($query) or die (mysql_error());
break;
}
}
This is incorrect PHP:
$query = "DELETE FROM cart_course_teetimes a WHERE h.IDDestination='.$id.'"
You're already inside a "-quoted string, so . PHP concatenation operators aren't operators, they're just a period.
You want either of these instead:
$query = "DELETE FROM cart_course_teetimes a WHERE h.IDDestination='" . $id . "'";
$query = "DELETE FROM cart_course_teetimes a WHERE h.IDDestination='$id'"
Right now you're producing
... WHERE h.IDDestination = .42.
which is not valid SQL.
Plus it appears you're mixing database libraries. You've got $conn->fetch() which implies you're using one of the OO db libraries (mysqli? pdo? custom wrapper?). But you then call mysql_query(). Unless you've EXPLICITLY called mysql_connect(), that delete query will never execute. Database connections made in one of the libraries are utterly useless in any of the other libraries.
I need to count the number of rows from different(!) tables and save the results for some kind of statistic. The script is quite simple and working as expected, but I'm wondering if it's better to use a single query with (in this case) 8 subqueries, or if I should use separate 8 queries or if there's even a better, faster and more advanced solution...
I'm using MySQLi with prepared statements, so the single query could look like this:
$sql = 'SELECT
(SELECT COUNT(cat1_id) FROM `cat1`),
(SELECT COUNT(cat2_id) FROM `cat2`),
(SELECT COUNT(cat2_id) FROM `cat2` WHERE `date` >= DATE(NOW())),
(SELECT COUNT(cat3_id) FROM `cat3`),
(SELECT COUNT(cat4_id) FROM `cat4`),
(SELECT COUNT(cat5_id) FROM `cat5`),
(SELECT COUNT(cat6_id) FROM `cat6`),
(SELECT COUNT(cat7_id) FROM `cat7`)';
$stmt = $db->prepare($sql);
$stmt->execute();
$stmt->bind_result($var1, $var2, $var3, $var4, $var5, $var6, $var7, $var8);
$stmt->fetch();
$stmt->free_result();
$stmt->close();
while the seperate queries would look like this (x 8):
$sql = 'SELECT
COUNT(cat1_id)
FROM
`cat1`';
$stmt = $db->prepare($sql);
$stmt->execute();
$stmt->bind_result($var1);
$stmt->fetch();
$stmt->free_result();
$stmt->close();
so, which would be faster or "better style" related to this kind of query (e.g. statistics, counter..)
My inclination is to put queries into the FROM rather than the SELECT, where possible. In this example, it requires a cross join between the tables:
select c1.val, c2.val . . .
from (select count(cat1_id) as val from cat1) c1 cross join
(select count(cat2_id as val from cat2) c2 cross join
. . .
The performance should be the same. However, the advantage appears with your cat2 table:
select c1.val, c2.val, c2.valnow, . . .
from (select count(cat1_id) as val from cat1) c1 cross join
(select count(cat2_id) as val
count(case when date >= date(now()) then cat2_id end)
from cat2
) c2 cross join
. . .
You get a real savings here by not having to scan the table twice to get two values. This also helps when you realize that you might want to modify queries to return more than one value.
I believe the cross join and select-within-select would have the same performance characteristics. The only way to really be sure is to test different versions.
The better way, is use just one query, because is only one conecction with database, instead of, if you use many queries, then are many conecctions with database, this process involves: coneccting and disconeccting, and this is more slower.
Just to follow up your comment, here is an example using one of my DBs. Using a prepared statement here buys you nothing. This multiple query in fact only executes one RPC to the D/B engine. All of the other calls are local to the PHP runtime system.
$db = new mysqli('localhost', 'user', 'password', 'blog');
$table = explode( ' ', 'articles banned comments config language members messages photo_albums photos');
foreach( $table as $t ) {
$sql[] = "select count(*) as count from blog_$t";
}
if ($db->multi_query( implode(';',$sql) )) {
foreach( $table as $t ) {
if ( ($rs = $db->store_result() ) &&
($row = $rs->fetch_row() ) ) {
$result[$t] = $row[0];
$rs->free();
$db->next_result(); // you must execute one per result set
}
}
}
$db->close();
var_dump( $result );
Just out of interest, I did an strace on this and the relevant four lines are
16:54:09.894296 write(4, "\211\1\0\0\3select count(*) as count fr"..., 397) = 397
16:54:09.895264 read(4, "\1\0\0\1\1\33\0\0\2\3def\0\0\0\5count\0\f?\0\25\0\0\0\10\201"..., 16384) = 544
16:54:09.896090 write(4, "\1\0\0\0\1", 5) = 5
16:54:09.896192 shutdown(4, 2 /* send and receive */) = 0
There was ~1 mSec between the query and the response to and from the MySQLd process (this is because this was on localhost, and the results were in its query cache, BTW).. and 0.8 mSec later the DB close was executed. And that's on my 4-yr old laptop.
Regarding to TerryE's example and the advice to use multi_query(!), I checked the manual and changed the script to fit my needs.. finally I got a solution that looks like this:
$sql = 'SELECT COUNT(cat1_id) as `cat1` FROM `cat1`;';
$sql .= 'SELECT COUNT(cat2_id) as `cat2` FROM `cat2`;';
$sql .= 'SELECT COUNT(cat2_id) as `cat2_b` FROM `cat2` WHERE `date` >= DATE(NOW());';
$sql .= 'SELECT COUNT(cat3_id) as `cat3` FROM `cat3`;';
$sql .= 'SELECT COUNT(cat4_id) as `cat4` FROM `cat4`;';
$sql .= 'SELECT COUNT(cat5_id) as `cat5` FROM `cat5`;';
$sql .= 'SELECT COUNT(cat6_id) as `cat6` FROM `cat6`;';
$sql .= 'SELECT COUNT(cat7_id) as `cat7` FROM `cat7`;';
if ($db->multi_query($sql))
{
do
{
if ($stmt = $db->store_result())
{
while ($row = $stmt->fetch_assoc())
{
foreach ($row as $key => $value)
{
$count[$key] = $value;
}
}
$stmt->free_result();
}
} while ($db->more_results() && $db->next_result());
}
There are some differences to TerryE's example, but the result is the same. I'm aware that there are 7 line at the beginning that are almost identical, but as soon as I need a WHERE clause or something else, I prefer this solution to a foreach loop where I'd need to add queries manually or use exceptions with if { ... } ...
As far as I can see, there should be no problem with my solution, or did I miss something?
The following MySQL query runs in PHP without errors, but the resultset is empty. Directly outputting the query string to a file and running the query in the MySQL client using 'source [filename]' returns several rows of results, as expected.
Is there something that would cause this query not to work with PHP? categorylinks.cl_to and smw_spec2.value_string are both varbinary(255). Show create table indicates engine=InnoDB and default charset=binary.
Things I have tried without success:
$sql = preg_replace("/[\n\t]+/", " ", $sql);
Changing '_wpg' and 'Derp' to CAST('_wpg' AS BINARY(255))
Changing '_wpg' and 'Derp' to BINARY '_wpg'
I am using the MediaWiki DatabaseMysql class to execute the query and fetch rows, but it's a very thin abstraction, and I'm certain it's not the problem (see below).
SELECT
prop.name AS prop_name, prop.count AS prop_count, prop.type AS prop_type,
val.value AS val_value, val.unit AS val_unit, val.count AS val_count
FROM
(
SELECT
s_id, name, type, COUNT(foo.name) AS count
FROM (
(
SELECT
cl.cl_to AS cat_name, s.smw_id AS s_id, s.smw_sortkey AS name, spec.value_string AS type
FROM `smw_ids` s
INNER JOIN (`categorylinks` cl, `page` p, `smw_ids` s2, `smw_atts2` a)
ON (cl.cl_from = p.page_id AND
p.page_title = s2.smw_title AND
s2.smw_id = a.s_id AND
a.p_id = s.smw_id)
LEFT JOIN `smw_spec2` spec ON s.smw_id = spec.s_id
)
UNION ALL
(
SELECT
cl.cl_to AS cat_name, s.smw_id AS s_id, s.smw_sortkey AS name, '_wpg' AS type
FROM `smw_ids` s
INNER JOIN (`categorylinks` cl, `page` p, `smw_ids` s2, `smw_rels2` a)
ON (cl.cl_from = p.page_id AND
p.page_title = s2.smw_title AND
s2.smw_id = a.s_id AND
a.p_id = s.smw_id)
)
) AS foo
WHERE foo.cat_name = 'Derp'
GROUP BY name
ORDER BY count DESC
LIMIT 10
) AS prop
INNER JOIN
(
SELECT
bar.p_id AS p_id, bar.value AS value, bar.unit AS unit, COUNT(bar.value) AS count,
IF( #prev != p_id, #rownum := 1, #rownum := #rownum+1 ) AS rank,
#prev := p_id
FROM (
(SELECT a.p_id AS p_id, a.value_xsd AS value, a.value_unit AS unit FROM `smw_atts2` a)
UNION ALL
(SELECT r.p_id AS p_id, s.smw_sortkey AS value, NULL AS unit
FROM `smw_rels2` r INNER JOIN `smw_ids` s ON r.o_id = s.smw_id)
) AS bar
GROUP BY value, unit
ORDER BY count DESC
) AS val
ON prop.s_id = val.p_id
WHERE val.rank <= 50
ORDER BY prop_count DESC, prop_name, val_count DESC, val_value
Edit: The following test script outputs nothing. query.sql contains exactly the query above, written to file immediately preceding the mysql_query() call in MediaWiki's database class.
$db = mysql_connect('localhost', 'root', '');
mysql_select_db('mediawiki', $db);
$res = mysql_query(file_get_contents("query.sql"), $db);
while ($row = mysql_fetch_assoc($res)) {
var_dump($row);
}
echo mysql_error($db);
Edit: I imported a huge database dump and afterwards, when I loaded the PHP page, there was a noticeable wait that seemed to indicate that the query was running, but still no results showed. I ended up reworking the query, and I no longer have this problem.
Try this to detect and report errors better:
$db = mysql_connect('localhost', 'root', '');
mysql_select_db('mediawiki', $db);
$res = mysql_query(file_get_contents("query.sql"), $db);
if (!$res) {
print "SQL Error ".mysql_errno().":".mysql_error().", from query: '".file_get_contents("query.sql")."'";
} else {
while ($row = mysql_fetch_assoc($res)) {
var_dump($row);
}
}
Try this after mysql_connect:
mysql_query('SET NAMES utf8;');