REQUESTED TABLE DEFINITIONS
Table "public.call_record"
Column | Type | Modifiers
-----------------+------------------------+---------------
cntrct_id | character varying(15) | not null
call_regard | text |
port_type | character varying(9) |
inst | text |
info_taken | character varying(40) |
log_date | date | not null
log_time | time without time zone | not null
act_taken | text |
use_material | text |
targ_pest | integer |
work_comp_by | text |
emp_no | integer |
comp_date | date |
job_start_time | time without time zone |
job_leave_time | time without time zone |
comp_val | boolean | default false
fti_call_regd | public.tsvector |
fti_inst | public.tsvector |
fti_act_take | public.tsvector |
route | character(3) |
act_port | text |
targ_pest_opt | text |
call_regard_opt | text |
targpest_other | text |
date_sched | date |
custord_num | integer |
dist_id | integer |
phone_slot | integer | default 0
Indexes:
"call_record_pkey" PRIMARY KEY, btree (cntrct_id, log_date, log_time)
"route_index" hash (route)
Check constraints:
"call_record_targ_pest_check" CHECK (targ_pest <= 100)
"call_record_targ_pest_check1" CHECK (targ_pest >= 0)
Table "public.per_call"
Column | Type | Modifiers
---------+----------------------+-----------
dist_id | character varying(2) |
route | character varying(2) |
type | character(1) |
total | integer |
I need to get data from 2 tables and print it in a single report. The report should look like this:
district | route | type | total | callbacks
| 01 | T | 12 | 5
| 02 | P | 0 | 0
| 03 | P | 3 | 1
2 | 01 | T | 4 | 1
| 02 | T | 1 | 0
| 03 | P | 0 | 0
etc... (this is theoretical sample data)
So, in essence I need to get the dist_id, route, type, and count(*) from the table per_call
and the count of call_backs from the table call_record
PROBLEM: looping through tables makes it go glacially slow. How can I adjust the following PSQL query so that I don't have to loop and I can echo the tabular data properly?
Let me know if anything is opaque and I will try to clarify
echo '<table align="center" border = 2>
<th>DISTRICT</th>
<th>ROUTE</th>
<th>TYPE</th>
<th>TOTAL</th>
<th>CALL BACKS</th>';
$SQL = " SELECT per_call.dist_id, per_call.route, per_call.type, per_call.total
FROM per_call, call_record
WHERE TRUE ";
if($type == 'termite'){
$SQL = $SQL." AND per_call.type = 'T' ";
}
else{
$SQL = $SQL." AND per_call.type = 'P' ";
}
$SQL = $SQL." AND call_record.dist_id = per_call.dist_id
AND call_record.log_date >= '$startDate'
AND call_record.log_date <= '$endDate'
ORDER BY per_call.dist_id, per_call.route, per_call.type ASC ";
echo $SQL;
/*AND call_record.log_date = '$startDate'
AND call_record.log_date = '$endDate'*/
$Q = pg_query($connect,$SQL);
while($row = pg_fetch_row($Q)){
$dist = $row[0];
$route = $row[1];
$type = $row[2];
$total = $row[3];
echo '<tr>';
echo '<td align="center">'.$dist.'</td>';
echo '<td align="center">'.$route.'</td>';
echo '<td align="center">'.$type.'</td>';
echo '<td align="center">'.$total.'</td>';
$SQL2 = "SELECT COUNT(*)
FROM call_record
WHERE dist_id = $dist
AND route = '$route'
AND substring(cntrct_id from 2 for 1) = '$type'
AND substring(call_regard_opt from 2 for 1) = '1'
";
$Q2 = pg_query($connect,$SQL2);
$row2 = pg_fetch_row($Q2);
$callbacks = $row2[0];
echo '<td align="center">'.$callbacks.'</td>';
echo '</tr>';
}
echo "</table>";
select pc.dist_id, pc.route, pc.type, pc.total,
count(
substring(cntrct_id from 2 for 1) = '$type'
AND substring(call_regard_opt from 2 for 1) = '1'
or null
) callbacks
from
per_call pc
inner join
call_record cr on cr.dist_id = pc.dist_id
where cr.log_date between '$startdate' and cr.log_date <= '$enddate'
group by pc.dist_id, pc.route, pc.type, pc.total
order by pc.dist_id, pc.route, pc.type asc
You don't have any indexes on dist_id in either table, which is going to make your join very slow. Add indexes on dist_id and see how much it improves.
Also, that query inside the loop is going to be the death of you, because you're going to be doing many many many queries. Work the inner query into your main query so you only execute one query in the database.
Unless index problems, your query is not badly written, so it can't be really optimized from a query form point of view. You can do that though which is cleaner :
$SQL = "SELECT per_call.dist_id, per_call.route, per_call.type, per_call.total
FROM per_call, call_record
WHERE call_record.dist_id = per_call.dist_id
AND per_call.type = '".($type == 'termite' ? "T" : "P")."'
AND call_record.log_date >= '$startDate'
AND call_record.log_date <= '$endDate'
ORDER BY per_call.dist_id, per_call.route, per_call.type"
Anyway, this is still vulnerable to SQL injections. Try using parameterized queries.
Related
I'm working on a venue programming system for festivals to update our current system of just using loads of spreadsheets. I'm trying to figure out a way to display a table that shows all subvenues related to a festival as table headings and then all timeslots related to that subvenue as table columns. I want it to look something like this at the end.
screenshot of current spreadsheet used:
The idea is that you will be able to click on one of the free timeslots, and open a modal to allocate a show to that slot or display shows already attached to it. Ideally each subvenue will be drag and dropped into order but these are problems for later.
So far I'm trying to use a loop to create a table with only 1 column. have an sql query return the header and then inside that return loop have another sql query that returns all of the timeslots, then close the first loop. but this is only displaying 1 table and not looping round to return the others.
Code is
<?php
//selects subvenue
$sql = "SELECT *
FROM Subvenue S
JOIN Venue V
ON S.venueId = V.venueId
JOIN festvenue FV
ON V.venueId = FV.venueId
WHERE FV.festId = $festId;";
$result = mysqli_query($conn, $sql);
if (!$result) die ("Database access failed");
$rows = $result->num_rows;
//starts loop to display subvenues
for ($j = 0 ; $j < $rows ; ++$j) {
$row = $result->fetch_array(MYSQLI_NUM);
$subvenueId = htmlspecialchars($row[0]);
$subvenueName = htmlspecialchars($row[2]);
echo <<<_END
<table>
<tr>
<th id="$subvenueId">$subvenueName<th>
</tr>
_END;
$sql = "SELECT * FROM TimeSlot
WHERE subVenId = $subvenueId
ORDER BY (start >= '05:00:00') desc, start;";
$result = mysqli_query($conn, $sql);
if (!$result) die ("Database access failed");
$rows = $result->num_rows;
for ($j = 0 ; $j < $rows ; ++$j) {
$row = $result->fetch_array(MYSQLI_NUM);
$timeId = htmlspecialchars($row[0]);
$type = htmlspecialchars($row[3]);
$start = htmlspecialchars($row[4]);
$end = htmlspecialchars($row[5]);
$length = htmlspecialchars($row[8]);
echo <<<_END
<tr id="$timeId" class="timeslot-time">
<td class="$type-$length">$start - $end</td>
</tr>
_END;
}
echo "</table>";
}
?>
The sample data I have is below
Subvenue Table
+----------+---------+------------------------+
| subVenId | venueId | subVenName |
+----------+---------+------------------------+
| 1 | 2 | Subvenue 1 |
| 2 | 2 | subvenue 2 |
+----------+---------+------------------------+
timeslot Table
+--------+--------+----------+-------+----------+----------+--------+
| timeId | festId | subVenId | type | start | end | length |
+--------+--------+----------+-------+----------+----------+--------+
| 1 | 11 | 1 | show | 12:00:00 | 13:00:00 | 60 |
| 2 | 11 | 1 | show | 13:30:00 | 14:30:00 | 60 |
| 3 | 11 | 1 | break | 13:00:00 | 13:30:00 | 30 |
| 4 | 11 | 1 | break | 14:30:00 | 15:00:00 | 30 |
| 5 | 11 | 1 | show | 15:00:00 | 16:00:00 | 60 |
| 6 | 11 | 2 | show | 16:30:00 | 17:30:00 | 60 |
| 7 | 11 | 2 | show | 18:00:00 | 19:00:00 | 60 |
| 8 | 11 | 2 | show | 19:30:00 | 20:30:00 | 60 |
| 9 | 11 | 1 | show | 21:00:00 | 22:00:00 | 60 |
| 10 | 11 | 2 | show | 22:30:00 | 23:30:00 | 60 |
+--------+--------+----------+-------+----------+----------+--------+
I'm not even sure a table it the best thing for this or would lists or something else be better?
At the end I want it to display
+-------------------+. +-------------------+
| subvenue 1 | | subvenue 2 |
+-------------------+. +-------------------+
| 12:00:00-13:00:00 | | 16:30:00-17:30:00 |
| 13:30:00-14:30:00 | | 18:00:00-19:00:00 |
| 13:00:00-13:30:00 | | 19:30:00-20:30:00 |
| 14:30:00-15:00:00 | | 22:30:00-23:30:00 |
| 15:00:00-16:00:00 |. +-------------------+
| 21:00:00-22:00:00 |
+-------------------+
etc
I've managed to figure this out. I decided to output a series of lists instead of using a table.
Apart from that my main fix was instead of using the same $sql and $stmt variables in the mySQL query I used $subSql and $subStmt for the second query.
require_once "header.php";
$festId = mysqli_real_escape_string($conn, $_GET["festival_Id"]);
?>
<div id="programming" class="tabcontent">
<h2 >Programming</h2><br>
<?php
//gets the subvenue and starts first loop
$sql = "SELECT *
FROM Subvenue S
JOIN Venue V
ON S.venueId = V.venueId
JOIN festvenue FV
ON V.venueId = FV.venueId
WHERE FV.festId = ?;";
$stmt = mysqli_stmt_init($conn);
if(!mysqli_stmt_prepare($stmt, $sql)) {
header("location: ../festival.php?error=sqlerror&festival_Id".$festId);
exit();
}
else {
mysqli_stmt_bind_param($stmt, "s", $festId);
mysqli_stmt_execute($stmt);
$result = $stmt->get_result();
$rows = $result->num_rows;
for ($j = 0 ; $j < $rows ; ++$j)
{
$row = $result->fetch_array(MYSQLI_NUM);
$subVenueId = htmlspecialchars($row[0]);
$subVenueName = htmlspecialchars($row[2]);
echo <<<_END
<div id="$subVenueId" class="programme">
<ul>
<li class="programme-heading">$subVenueName</li>
<ul>
_END;
//select all timeslots with that subvenue
$subSql = "SELECT * FROM TimeSlot
WHERE subVenId = ?
ORDER BY (start >= '05:00:00') desc, start;";
$subStmt = mysqli_stmt_init($conn);
if(!mysqli_stmt_prepare($subStmt, $subSql)) {
header("location: ../festival.php?
error=sqlerror&festival_Id".$festId);
exit();
}
else {
mysqli_stmt_bind_param($subStmt, "s", $subVenueId);
mysqli_stmt_execute($subStmt);
$subResult = $subStmt->get_result();
while ($row = mysqli_fetch_assoc($subResult)) {
$timeId = htmlspecialchars($row['timeId']);
$type = htmlspecialchars($row['type']);
$start = htmlspecialchars($row['start']);
$end = htmlspecialchars($row['end']);
$length = htmlspecialchars($row['length']);
echo <<<_END
<li id="[$timeId" class="$type-$length">$start - $end</li>
_END;][1]
}
}
echo "</ul></ul></div>";
}
}
That looks exactly how I wanted it. in the initial question
Column title has a lot of duplicated values, more than once.
I need to update the column so, for example if 'gold' is duplicated - it becomes 'gold 1', 'gold 2', etc.
Something like this:
$st = $db->query("select id, title from arts order by title asc");
$st->execute();
$x = 0;
while($row = $st->fetch()){
$title = $row['title'];
//if($title.is duplicated){
$x++;
$title .= ' ' . $x;
$stb = $db->query("update arts set title = '" . $title . "' where id = " . $row['id']);
$stb->execute();
}
}
Any help?
It would be more efficient to do this in pure SQL rather than using PHP. Here is an approach that uses window functions, available in MySQL 8.0.
You can use a subquery to count how many title duplicates exists for each record, and assign a rank to each record within groups of records having the same title. Then, you can JOIN the subquery with the table to update. Where more than one record exists, you can append the row number to every record in the group.
Query:
UPDATE arts a
INNER JOIN (
SELECT
id,
title,
COUNT(*) OVER(PARTITION BY title) cnt,
ROW_NUMBER() OVER(PARTITION BY title ORDER BY id) rn
FROM arts
) b ON a.id = b.id
SET a.title = CONCAT(a.title, b.rn)
WHERE cnt > 1;
Demo on DB Fiddle
Sample data:
| id | title |
| --- | ------ |
| 10 | silver |
| 20 | gold |
| 30 | gold |
| 40 | bronze |
| 50 | gold |
| 60 | bronze |
Results after running the update query:
| id | title |
| --- | ------- |
| 10 | silver |
| 20 | gold1 |
| 30 | gold2 |
| 40 | bronze1 |
| 50 | gold3 |
| 60 | bronze2 |
Please see below code that working for me
// Create connection
$conn = new mysqli($servername, $username, $password,$dbname);
// get all row
$sql = "select id, title from arts order by title asc";
$result = $conn->query($sql);
while ($row=$result->fetch_assoc()) {
$title=$row['title'];
// select where title is same
$sql = "select * from arts where title='".$title."'";
$result2 = $conn->query($sql);
// if number of row is greater then one
if ($result2->num_rows > 1){
$x=0;
while ($row2=$result2->fetch_assoc()) {
$id=$row2['id'];
// skip first row
if($x>0){
$newTitle=$title.' '.$x;
$uquery = "update arts set title='".$newTitle."' where title='".$title."' and id=$id";
$update = $conn->query($uquery);
}
$x++;
}
}
}
and after query run
This works in MySql 5.7:
update arts a inner join (
select * from (
select t.id,
(
select count(*) + 1 from arts
where id < t.id and title = t.title
) counter
from arts t
) t
) t on t.id = a.id
set a.title = concat(a.title, ' ', t.counter)
where a.title in (
select h.title from (
select title from arts
group by title
having count(*) > 1
) h
);
See the demo.
For data:
| id | title |
| --- | -------- |
| 1 | silver |
| 2 | gold |
| 3 | diamond |
| 4 | bronze |
| 5 | gold |
| 6 | bronze |
| 7 | gold |
the result is
| id | title |
| --- | -------- |
| 1 | silver |
| 2 | gold 1 |
| 3 | diamond |
| 4 | bronze 1 |
| 5 | gold 2 |
| 6 | bronze 2 |
| 7 | gold 3 |
I think It would be more efficient to do this in SQL too, but you may can do a function to validate the duplicate, something like this:
function isDuplicated( $title, $db ){
$dp = $db->query("SELECT * FROM arts WHERE title = $title");
if ( $dp->num_rows > 1)
return true;
return false;
}
$st = $db->query("select id, title from arts order by title asc");
$st->execute();
$x = 0;
while($row = $st->fetch()){
$title = $row['title'];
if( isDuplicated( $title, $db ) ){
$x++;
$title .= ' ' . $x;
$stb = $db->query("update arts set title = '" . $title . "' where id = " . $row['id']);
$stb->execute();
}
}
I have a MySQL table like this:
| rsid | rsuser | rsintime | rsouttime | rsroom |
| ---- | ------- | ---------------- | ---------------- | ------ |
| 1 | Nick S | 10/14/2014 11:17 | 10/14/2014 12:18 | 1 |
| 2 | Mike G | 10/15/2014 10:18 | 10/15/2014 11:19 | 1 |
| 3 | Chuck M | 10/14/2014 21:56 | 10/14/2014 22:56 | 1 |
| 4 | Jake B | 10/26/2014 22:14 | 10/26/2014 23:15 | 1 |
My PHP code is:
<?php
$con=mysqli_connect("0.0.0.0","roomapp","hi","roomapp");
$testschedulesql = "SELECT (NOW() > rsintime AND NOW() < rsouttime) as res_rm from raRmSchedule";
$testscheduleqry = mysqli_query($con, $testschedulesql);
$testschedule = mysqli_num_rows($testscheduleqry);
$testscheduletext = mysqli_fetch_array($testscheduleqry);
echo $testschedule;
if($testschedule > 0){
echo 'Busy';
}
else{
echo 'Not';
}
?>
However, $testschedule always returns the total rows. I want it to return only rows where the current time is within an in/out time. What am I doing wrong?
You need a where clause:
SELECT rm.*
from raRmSchedule
WHERE (NOW() > rsintime AND NOW() < rsouttime)
To count the number of rows:
SELECT COUNT(*)
from raRmSchedule
WHERE (NOW() > rsintime AND NOW() < rsouttime)
This returns one row with one column. It will be 0 if there are no matches.
Your version returns one row for each row in the table. There will be one column with a value of 0 or 1, depending on whether the condition matches.
Of course it will give you all rows, as what you are selecting is the boolean value of an expression.
I think what you want is giving the query a WHERE clause to filter the time you're needing.
Something like
Select * from raRMSchedule where (now() > rsintime and now() < rsouttime).
I have a MySQL table like this:
| rsid | rsuser | rsintime | rsouttime | rsroom |
| ---- | ------ | ------------------- | ------------------- | ------ |
| 1 | Nick S | 2014-10-14 11:17:34 | 2014-10-14 12:18:06 | 1 |
| 2 | Mike G | 2014-10-15 10:18:38 | 2014-10-15 11:19:00 | 1 |
I want to search with the current time against the in/out times. So, in English, if the room is busy, echo "Busy". If it is available, echo "Room is free until " the next in time.
Here is what I have so far:
<?php
$con=mysqli_connect("008.178.143.7","roomapp","password","roomapp");
$testschedulesql = "SELECT (NOW() > rsintime AND NOW() < rsouttime) as res_rm from raRmSchedule";
$testscheduleqry = mysqli_query($con, $testschedulesql);
$testschedule = mysqli_num_rows($testscheduleqry);
if($testschedule > 0){
echo 'Busy';
}
else{
echo 'Not';
}
?>
This always returns as true.
If you were to solve it in MySQL it could be something like this:
SELECT IF (NOW() > rsintime AND NOW() < rsouttime)
THEN 'Busy'
ELSE
THEN CONCAT('Room is free until ',
(SELECT rsintime
FROM rooms
WHERE rsid <> rsintime
ORDER BY rsintime
LIMIT 0, 1))
AS room_status
WHERE rsid = theIdOfTheRoom
This is just a guide though. I'm not sure if it works because I can't test it now and a better one could probably be written to solve this. But hopefully it can get you rolling.
I have php-script running on top of apache. Every time when user goes to specific URL, he/she will get csv-file.
Column names are fetched like this (thanks to Daniel Figueroa :)
$csv_output .= "\n";
// get the column name from the first DB (ins.data)
mysql_select_db($db, $link) or die("Can not connect to DB1.");
$result = mysql_query("SHOW COLUMNS FROM ".$table." WHERE Field NOT IN
('ID','Key','Text')");
$i = 0;
if (mysql_num_rows($result) > 0) {
while ($row = mysql_fetch_assoc($result)) {
$csv_output .= $row['Field']."; ";
$i++;
}
}
// get the column names from the second DB (Cu.data)
mysql_select_db($db2, $link) or die("Can not connect to DB2.");
$result = mysql_query("SHOW COLUMNS FROM ".$table2." ");
;
$i = 0;
if (mysql_num_rows($result) > 0) {
while ($row = mysql_fetch_assoc($result)) {
$csv_output .= $row['Field']."; ";
$i++;
}
}
$csv_output .= "\n";
Actual query on PHP-script goes like this:
$values = mysql_query(" SELECT ins.data.`Date`, ins.data.`Number`,
ins.data.`Email`, ins.data.`TargetId`, ins.data.`CSW`,
ins.data.`TSW`, ins.data.`CType`,
Cu.data.`Cus`, Cu.data.`Co`,Cu.data.`Ci`,
Cu.data.`SID`, Cu.data.`SType`
FROM ins.data
LEFT JOIN Cu.data ON (ins.data.TargetId = Cu.data.TargetID)
ORDER BY ins.data.ID DESC");
Output of 'desc':
mysql> desc ins.data;
+-------------------+------------------+------+-----+---------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+------------------+------+-----+---------------------+----------------+
| ID | int(10) unsigned | NO | PRI | NULL | auto_increment |
| Date | timestamp | NO | | 0000-00-00 00:00:00 | |
| Number | text | NO | | NULL | |
| Text | text | NO | | NULL | |
| Email | text | NO | | NULL | |
| TargetId | varchar(20) | NO | | NULL | |
| CSW | text | NO | | NULL | |
| TSW | text | NO | | NULL | |
| Key | text | NO | | NULL | |
| CType | text | NO | | NULL | |
+-------------------+------------------+------+-----+---------------------+----------------+
10 rows in set (0.00 sec)
mysql> desc Cu.data;
+----------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+---------------+------+-----+---------+-------+
| Title | decimal(15,0) | NO | | NULL | |
| Cu | text | NO | | NULL | |
| Co | text | NO | | NULL | |
| Ci | text | NO | | NULL | |
| SID | text | NO | | NULL | |
| TargetID | varchar(20) | NO | MUL | NULL | |
| SType | text | NO | | NULL | |
| empty1 | int(11) | NO | | NULL | |
| empty2 | int(11) | NO | | NULL | |
| empty3 | int(11) | NO | | NULL | |
| empty4 | int(11) | NO | | NULL | |
| empty5 | int(11) | NO | | NULL | |
| empty6 | int(11) | NO | | NULL | |
| empty7 | int(11) | NO | | NULL | |
+----------+---------------+------+-----+---------+-------+
12 rows in set (0.00 sec)
UPDATE 3:
This is no more NATURAL LEFT JOIN-issue. Replaced with LEFT JOIN.
Added fields empty1-5 to ins.data to get data to csv-file. Without fields empty1-5, only data from first db (ins.data) was on csv.file.
Now i have data on all fields but field (or column names on excel) names on csv are on wrong order and not wanted fields (columns) are visible like Title and empty1-5.
Any ideas how to fix this? Some other way to get Field names to csv-file without "SHOW COLUMNS"?
I could write with 'echo' in the beginning of csv-file values what i want. ie
"Date; Number; Email; TargetID, CSW; TSW; CType; Cu; SID; Co; Ci; SType;" but i am so newbie with PHP that i don't know how :(
Another issue is that if field ID is first column on excel, excel cannot handle that and it must be excluded from SHOW COLUMNS output.
UPDATE4: Added more empty-fields to DB2 (Cu.data) and reordered SQL-query, now all values are visible and on right order.
EDIT
First of all, your table naming schema is weird and unusual... but assuming I understand it correctly then this query should work (if it does not then rename your tables without the dots (periods) to make things less confusing:
mysql_query('SELECT ins.data.Date, ins.data.Number, ins.data.Email, ins.data.TID, ins.data.CSW, ins.data.TSW, ins.data.CType, CU.data.SID, cu.data.SType, cu.data.CU, cu.data.CO, cu.data.Ci, FROM ins.data, cu.data ORDER BY ins.data.ID DESC');
According to to the mysql_query function reference, data should not end with a semicolon when using mysql_query in PHP... i never put the semicolon in there so I don't ever have a problem, that's what I initially noticed with your script (as I said i've never tried it so I don't know if thats the issue). Should be:
$values = mysql_query("SELECT Date, Number, Email, TID, CSW, TSW, CType, SID, SType, Cu, Co, Ci FROM ins.data NATURAL LEFT JOIN Cu.data ORDER BY ID DESC");
Also, when doing JOINS, usually you specify what column belongs to what table... like in the standard mysql example here:
<?php
// Make a MySQL Connection
// Construct our join query
$query = "SELECT family.Position, food.Meal ".
"FROM family LEFT JOIN food ".
"ON family.Position = food.Position";
$result = mysql_query($query) or die(mysql_error());
// Print out the contents of each row into a table
while($row = mysql_fetch_array($result)){
echo $row['Position']. " - ". $row['Meal'];
echo "<br />";
}
?>
I don't always use the JOIN commands either... you can use an alternative syntax like so:
mysql_query('SELECT ... FROM t1, t2, t3 WHERE t1.b = t2.b AND t2.c = t3.c AND t1.a = t3.a');
Try putting aliases on table names. It will make the query more readable, too:
SELECT
i.`Date`, i.Number, i.Email, i.TID, i.CSW, i.TSW, i.CType,
c.Cu, c.Co, c.Ci, c.SID, c.TID, c.SType
FROM
ins.data AS i
LEFT JOIN
Cu.data AS c
ON i.TID = c.TID
ORDER BY
i.ID DESC ;