SQL - select both "child" and "parent" rows in a single query - mysql

I have a database filled with a lot of "empty" records and I want to get rid of those orphaned records.
The database is structured like this;
A child row that contains the real entry (meta_value)
A parent row that is "linked" to its child (by it's meta_key)
| meta_id post_id meta_key meta_value |
| |
| 011 301 ACF__P_01_01 Foo |
| 012 309 _ACF__P_01_01 field_5874d5 |
| 013 321 ACF__P_01_02 |
| 014 316 _ACF__P_01_02 field_54290a |
| 015 119 ACF__P_01_03 Bar |
| 016 101 _ACF__P_01_03 field_a04a88 |
| 017 119 ACF__P_01_03 |
| 018 101 _ACF__P_01_03 field_a04a88 |
| 019 149 ACF__P_01_03 |
| 020 111 _ACF__P_01_03 field_a04a88 |
| 021 169 ACF__P_01_03 Foo Bar |
| 022 171 _ACF__P_01_03 field_a04a88 |
It's easy to select all the (empty) "child" rows, with a query like this;
SELECT
*
FROM
wp_postmeta
WHERE
wp_postmeta.meta_key LIKE '%ACF__%'
AND
wp_postmeta.meta_value LIKE ''
But this query only fetches the (empty) "child" rows, not their "parent" row.
There are two logical conditions that bind the parent to the child;
The first row is always the child, immediately followed by its parent.
The parent row has the same "meta_key" value as it's child, but pre-fixed by an underscore.
Is there any way to create a SQL query that selects the empty child records (like I did above) and it's parent as well?
I prefer a single query to fetch them both - but when that's not possible, I could run two queries as well (first one to fetch the parent, second one to get the empty children, I guess).
I use Navicat for my database management, so it must be plain SQL - no PHP.
The query should spit out the results like this;
| meta_id post_id meta_key meta_value |
| |
| 013 321 ACF__P_01_02 |
| 014 316 _ACF__P_01_02 field_54290a |
| 017 119 ACF__P_01_03 |
| 018 101 _ACF__P_01_03 field_a04a88 |
| 019 149 ACF__P_01_03 |
| 020 111 _ACF__P_01_03 field_a04a88 |
So both the empty-childs (no value in the "meta_value") and it's parent (both have the same "meta_key", where the parent has an underscore.

With a self-join
Select *
from wp_postmeta c
join wp_postmeta p
on p.meta_key = '_' + c.meta_key
The above will only fetch "pairs" of parent-child records. If it's possible to have parents without children (I assume it's not possible to have children without parents), and you want all the parents, including the childless ones, use an outer join
Select *
from wp_postmeta p
left join wp_postmeta c
on '_' + c.meta_key = p.meta_key
In relational databases, there is no such concept as "after" or "before" unless you define it yourself and add data into the tables that implements that concept (like a datetimestamp, or sequentially increasing numeric key, or whatever. Without such artifacts, records in a relational database table do not have any implicit order.

Using PHP I was able to solve it. I iterate over the first result from a query (that fetches all the empty children) and fetch every row AFTER that child, inside the loop.
But that's PHP - I wanted pure SQL :)
<?php
$connection = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if ($connection -> connect_errno > 0) {
die ("Unable to connect to database [" . $connection->connect_error . "]");
}
$query_1 = "SELECT * FROM `wp_postmeta` WHERE `meta_key` LIKE '%ACF%__' AND `meta_value` LIKE ''";
if (!$result_1 = $connection -> query($query_1)) {
die ("There was an error running query[" . $connection -> error . "]");
}
if ($result_1) {
$i = 0;
echo "<h2>ACF - empty fields</h2>";
echo "<table border=1 cellpadding=4 cellspacing=0 width=800>";
echo "<tr style='background:#eee;font-weight:bold;'><td>row</td><td>meta_id</td><td>post_id</td><td>meta_key</td><td>meta_value</td></tr>";
while ($row_1 = $result_1 -> fetch_assoc()) {
$i++;
echo "<tr>";
echo "<td width=100>" . sprintf('%03d', $i) . "</td>";
echo "<td width=100 style='background:#aaa;color:#fff;font-weight:bold;'>" . $row_1["meta_id"] . "</td>";
echo "<td width=100>" . $row_1["post_id"] . "</td>";
echo "<td width=100>" . $row_1["meta_key"] . "</td>";
echo "<td width=500>" . $row_1["meta_value"] . "</td>";
echo "</tr>";
$query_2 = "SELECT * FROM `wp_postmeta` WHERE `meta_id` = " . ($row_1["meta_id"] + 1);
if (!$result_2 = $connection -> query($query_2)) {
die ("There was an error running query[" . $connection -> error . "]");
}
while ($row_2 = $result_2 -> fetch_assoc()) {
$i++;
echo "<tr>";
echo "<td width=100>" . sprintf('%03d', $i) . "</td>";
echo "<td width=100 style='background:#aaa;color:#fff;font-weight:bold;'>" . $row_2["meta_id"] . "</td>";
echo "<td width=100>" . $row_2["post_id"] . "</td>";
echo "<td width=100>" . $row_2["meta_key"] . "</td>";
echo "<td width=500>" . $row_2["meta_value"] . "</td>";
echo "</tr>";
}
mysqli_free_result($result_2);
}
mysqli_free_result($result_1);
echo "</table>";
} else {
echo "No entries found.";
}
?>
- edit -
This PHP code is better, I guess... It uses only 2 queries and iterates over the first one...
It works perfectly, but still isn't pure SQL - I want to get rid of the PHP...
<?php
/* --------------------------------------------------- */
$connection = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if ($connection -> connect_errno > 0) {
die ("Unable to connect to database [" . $connection->connect_error . "]");
}
/* --------------------------------------------------- */
$query = "SELECT `meta_id` FROM `wp_postmeta` WHERE `meta_key` LIKE '%ACF%__' AND `meta_value` LIKE ''";
if (!$result = $connection -> query($query)) {
die ("There was an error running query[" . $connection -> error . "]");
}
if ($result) {
$array = array();
while ($row = $result -> fetch_assoc()) {
$array[] = $row["meta_id"];
$array[] = $row["meta_id"] + 1;
}
mysqli_free_result($result);
/* --------------------------------------------------- */
$query = "SELECT * FROM `wp_postmeta` WHERE `meta_id` IN (" . implode(",", array_map("intval", $array)) . ")";
if (!$result = $connection -> query($query)) {
die ("There was an error running query[" . $connection -> error . "]");
}
if ($result) {
$i = 0;
echo "<h2>ACF - empty fields</h2>";
echo "<table>";
echo "<tr><td>record</td><td>meta_id</td><td>post_id</td><td>meta_key</td><td>meta_value</td></tr>";
while ($row = $result -> fetch_assoc()) {
$i++;
echo "<tr>";
echo "<td>" . sprintf('%03d', $i) . "</td>";
echo "<td>" . $row["meta_id"] . "</td>";
echo "<td>" . $row["post_id"] . "</td>";
echo "<td>" . $row["meta_key"] . "</td>";
echo "<td>" . $row["meta_value"] . "</td>";
echo "</tr>";
}
mysqli_free_result($result);
echo "</table>";
if (isset($_GET["delete"])) {
echo "<span>all records deleted</span>";
} else {
echo "<a href='?delete'>delete empty records</a>";
}
}
/* --------------------------------------------------- */
} else {
echo "No entries found.";
}
?>

Related

MySQL Outer Join 3 Tables

I have 3 sql tables and I want to make 1 table in html
The tables are:
Norm (norm_id, name, description, cluster_cluster_id, orden_orden_id)
Cluster (cluster_id, cluster_name)
Orden (orden_id, orden_name)
The table must contain:
norm_id - norm_name - norm - description - cluster_name - orden_name
I think i need to do this with a left outer join?
And how to show it in a table?
Right now i have
while($row = mysqli_fetch_array($result)){
echo "<tr>";
echo "<td>" . $row['norm_id'] . "</td>";
echo "<td>" . $row['norm_name'] . "</td>";
echo "<td>" . $row['description'] . "</td>";
echo "<td>" . $row['cluster_name'] . "</td>";
echo "<td>" . $row['orden_name'] . "</td>";
echo "</tr>";
}
You can make left outer join like, SELECT column-names
FROM table-name1 LEFT JOIN table-name2
ON column-name1 = column-name2
WHERE condition

YII2 get the date after 10 years and 5 years

Here is the table structure.
tbl_staff
firstname varchar(50)
midname varchar(50)
lastname varchar(50)
...
tbl_staff2
first_day_of_service date
...
Here is the scenario.
For the first 10 years of stay of the staff in organization, he/she will be given a loyalty award and then every 5 years after he/she receives the first 10 years.
My initial code will just get the name of staff and the date of first day of service.
public function actionGet_loyalty() {
$query = (new \yii\db\Query())
->select(['firstname', 'midname', 'lastname', 'first_day_service'])
->from('tbl_staff')
->limit(100)
->offset(0)
->orderBy('first_day_service')
->leftJoin('tbl_staff2', 'tbl_staff2.staff_id = tbl_staff.id');
$command = $query->createCommand();
$data = $command->queryAll();
$string = "<table class='table table-striped'><tr><th>No.</th><th>Name</th><th>Date to Receive Loyalty Award</th></tr>";
$i = 1;
foreach ($data as $row) {
$firstname = $row['firstname'];
$midname = $row['midname'];
$lastname = $row['lastname'];
//$string.=$row['first_day_service'];
$string.="<tr>"
. "<td>$i</td>"
. "<td>" . $firstname . " " . $midname[0] . ". " . $lastname . "</td>"
. "<td>" . $row['first_day_service'] . "</td>"
. "</tr>";
$i++;
}
$string.="</table>";
return $string;
}
How to display all the name of staff and the date he/she will receive the award in increasing order.
Or any mysql query that will sort the name according the date?
If you want to do it via SQL query, in MySQL you can add date using DATE_ADD function:
DATE_ADD(date, INTERVAL 5 YEAR)
Check this similar question.
Or via PHP using DateTime modify method:
$dateTime = new DateTime($date);
$dateTime->modify('+ 5 years')->format('Y-m-d H:i:s');

Changing date format from database

I am trying to change the date format before it is displayed with SQL query however the date format is being completely ignored.
my code is
$query = "SELECT * , DATE_FORMAT(formatted, '%d/%m/%Y') from movies;";
then further down this is my table
echo "<table>"
echo "<table border='2'>"
echo "<tr>
<th>id</th>
<th>title</th>
<th>date</th>
</tr>";
while($row = mysql_fetch_array($query))
{
echo "<tr>";
echo "<td>" . $row['id'] . "</td>";
echo "<td>" . $row['title'] . "</td>";
echo "<td>" . $row['formatted'] . "</td>";
echo "</tr>";
}
echo "</table>";
?>
this query is working, however the date format is being ignored and just displaying the date in yyyy-mm-dd I want it in DD-MM-YY.
thanks
Use an alias to name your calculated column
SELECT * , DATE_FORMAT(datetime, '%d/%m/%Y') AS formatted_date
from movies
Use a different name than the existing column to differ between the two. Then use
echo "<td>" . $row['formatted_date'] . "</td>";
to get the formatted one.
You need to mention the alias for the formatted datetime column other wise formatted value will not be called in your code
SELECT * ,
DATE_FORMAT(`datetime`, '%d/%m/%Y') `datetime`
from movies

combine two pdo queries showing all results from the 2nd.

I had one table listing events and attendies. To try Database normalization i split this into two tables.
I have made a database listing all the planned games for a team.
Table 1
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+name + organiser + date + location + cost + notes + id+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
i then have a second database that has everyone how has marked they will be comming
Table 2
++++++++++++++++++++++++++
+id + event + player +
++++++++++++++++++++++++++
The id is unique to each, The id from table 1 is used as the event in table 2.
i have a simple PDO query that pulls the data from table 1 into a HTMl table
if($db->connect_error) {
die("Connection error: ".$db->connect_error);
}
$sql = $db->query('SELECT * FROM event ORDER BY `date` ASC'
) or die($db->error);
echo"";
while($row = mysqli_fetch_assoc($sql))
{
$a = $row['attendees'];//will look up attendies to tick a check box if already aknowledged
$b = htmlentities($_SESSION['user']['username'], ENT_QUOTES, 'UTF-8');
if (strpos($a,$b) !== false) {
$c = "checked='checked'";
}else{
$c = "";
}
$r=$row['id'];
echo "<div id='results'>";
echo "<CENTER>";
echo "<table BORDER=6 class='fixed'>";
echo "<TR> ";
echo "<TD COLSPAN=3 ALIGN=CENTER><form action='going.php' method='post' name='form".$r."'>Event ".$row['name']." i'm going
<input type='checkbox' name='going'".$c." onclick='document.form".$r.".submit ();'>
<input type='text' name='Organise' value='".$r."'>
<input type='text' name='name' value='".$b."'>
</form></TD>";
echo "</TR> ";
echo "<TR>";
echo "<td>";
echo "<B><u><font color='#0080FF'>Title </font></b></u>".$row['name']."<br></font>";
echo "<B><u><font color='#0080FF'>Orginiser </font></b></u>".$row['organiser']."<br></font>";
echo "<B><u><font color='#0080FF'>When </font></b></u>".date("D j-M-Y GA",$row['dt'])."<br></font>";
echo "<B><u><font color='#0080FF'>Location </font></b></u>".$row['location']."<br></font>";
echo "<B><u><font color='#0080FF'>Cost </font></b></u>£".$row['cost']."<br></font></TD>";
echo "<TD ROWSPAN=3 valign='top'><B><u><font color='#0080FF'>Attendies </font></b></u>".$row['attendees']."<br></font></TD>";//will change to table 2
echo "<TD ROWSPAN=3 valign='top'><B><u><font color='#0080FF'>notes </font></b></u>".$row['notes']."<br></font></TD>";
echo "</tr>";
echo "</table>";
echo "</CENTER>";
echo "</div>";
}
i have tried and joind these using
$sql = $db->query('SELECT t1.*, t2.event as t2event, t2.player as player
FROM `event` as t1
LEFT JOIN `going` as t2 on t1.id = t2.event
ORDER BY t1.`dt` ASC'
but all i got was a HTML table per event and per player. I'm sure its possible but can't work it out, can i create a html table from quering table 1 and add to attendies all those going from table 2 not just one or creating a result each one. ?

MySQL: WP usermeta table: get types, then names, then sort

I think this is a cross tab job, but I'm not seeing it! In the Wordpress usermeta table, the data is like this:
| userid | meta_key | meta_value
| 123 | grant | Education
And
| userid | meta_key | meta_value
| 123 | orgname | ABC School
I need to search the table for all grant types of, say, 'Education', and then sort the results so that the org names are alphabetical.
The net result should effectively be a data set along these lines:
| userid | grant | orgname
| 123 | Eductaion | ABC School
How can I do that?
Thanks in advance!
Please note: these pages are not within wp itself, but are using the wp db. Thanks.
Check this if it's what you want.
<?php
$query = "SELECT userid ,meta_key, meta_value
FROM table
GROUP BY userid ";
$row = mysql_query($sql) or die(mysql_error());
$data = $array();
$i=0;
while( $res = mysql_fetch_array($row) )
{
$data[$i] = $res;
$i++;
}
echo "<table>";
for($i=0;$i<=count($data);$i+=2)
{
echo "<tr>";
echo "<td>userid </td>";
echo "<td>".$data[$i]['meta_key']."</td>";
echo "<td>".$data[$i+1]['meta_key']."</td>";
echo "</tr>"
$sql= "SELECT userid ,meta_key, meta_value
FROM table
WHERE userid = '".$data[$i]['userid']."' ";
$row_sub = mysql_query($sql) or die(mysql_error());
$j=0;
while( $resSub = mysql_fetch_array($row_sub ) )
{
$subData[$j] = $resSub ;
$j++;
}
for( $j=0;$j<=count($subData[$j]);$j+=2 )
{
echo "<tr>";
echo "<td>".$subData[$j]['userid ']."</td>";
echo "<td>".$subData[$j]['meta_value']."</td>";
echo "<td>".$subData[$j+1]['meta_value']."</td>";
echo "</tr>"
}
}
echo "</table>";
?>
<?php
$all_meta_for_user = get_user_meta( 123 );
echo "<pre>";
print_r( $all_meta_for_user ); // get all meta data of a particular user
exit;
?>
look here for more Get User Meta