I am trying to make a full-stack CRUD web app using mysql through phpmyadmin, which will be sent to an express front end (this is my first time ever working with SQL and PHP so pardon my mistakes or my lack of knowledge) For example, my table in PHP:
ID Name Time Date
1 COR 01:40:37 01/24/24
2 BING 01:39:38 01/23/24
whose values have been inserted with user input. Is the following possible?
First by getting the row number and sorting by ‘time’:
ID Name Time Date Rn
2 BING 01:39:38 01/23/24 1
1 COR 01:40:37 01/24/24 2
However I only want the following output when ‘BING’ is searched:
ID Name Time Date Rn
2 BING 01:39:38 01/23/24 1
My search function is:
async searchByName(name) {
try{
const response = await new Promise((resolve, reject) => {
const query = "SELECT *, ROW_NUMBER() OVER (ORDER BY time) rn FROM medley_relay; SELECT * FROM medley_relay WHERE name = ?;";
// try separating the two queries
connection.query(query, [name], (err, results) => {
if (err) reject(new Error(err.message));
resolve(results);
})
});
console.log(response)
return response;
} catch (error) {
console.log(error);
}
}
This is called by:
app.get('/search/:name', (request, response) => {
const {name} = request.params;
const db = dbService.getDbServiceInstance();
const result = db.searchByName(name);
result
.then(data => response.json({data : data}))
.catch(err => console.log(err));
console.log(result);
})
and activated through the front end with an HTML input element:
const searchBtn = document.querySelector('#search-btn');
searchBtn.onclick = function() {
const searchValue = document.querySelector('#search-input').value;
fetch('http://localhost:5000/search/' + searchValue)
.then(response => response.json())
.then(data => loadHTMLTable(data['data']));
}
My loadHTMLTable function is:
function loadHTMLTable(data) {
const table = document.querySelector('table tbody');
console.log(data);
if (data.length === 0) {
table.innerHTML = "<tr><td class='no-data'>No Data</td><td class = 'no-data'colspan='5'>No Data</td></tr>";
return;
}
let tableHtml = "";
data.forEach(function ({id, name, time, date, rn}) {
tableHtml += "<tr>";
tableHtml += `<td>${rn}</td>`;
tableHtml += `<td>${name}</td>`;
tableHtml += `<td>${time}</td>`;
tableHtml += `<td>${new Date(date).toLocaleDateString()}</td>`;
tableHtml += `<td><button class="delete-row-btn" data-id=${id}>Delete</td>`;
tableHtml += `<td><button class="edit-row-btn" data-id=${id}>Edit</td>`;
tableHtml += "</tr>";
});
table.innerHTML = tableHtml;
}
If I only use the query:
const query = "SELECT * FROM medley_relay WHERE name = ?;";
and name is passed through, I get an undefined value for rn because it hasn’t been defined through the ROW_NUMBER function. However when I do add the ROW_NUMBER FUNCTION I get:
[
[
RowDataPacket {
id: 15,
name: 'Corning',
time: '01:40:15',
date: 2024-04-10T04:00:00.000Z,
rn: 1
},
RowDataPacket {
id: 16,
name: 'Binghamton',
time: '01:52:42',
date: 2024-04-02T04:00:00.000Z,
rn: 2
}
],
[
RowDataPacket {
id: 16,
name: 'Binghamton',
time: '01:52:42',
date: 2024-04-02T04:00:00.000Z
}
]
]
with undefined values across my entire table. Is there a way to do this without getting a nested array and only the array at index [0][1]? Right now my query to load the data from the array without restricting it to any name looks like:
app.get('/getAll', (request, response) => {
const db = dbService.getDbServiceInstance();
const result = db.getAllData();
result
.then(data => response.json({data : data}))
.catch(err => console.log(err));
})
and
async getAllData() {
try{
const response = await new Promise((resolve,reject) => {
const query = "SELECT *, ROW_NUMBER() OVER (ORDER BY time) rn FROM medley_relay ORDER BY rn;"
"SELECT * FROM medley_relay;";
connection.query(query, (err,results) => {
if (err) reject(new Error(err.message));
resolve(results);
})
});
return response;
} catch (error) {
console.log(error);
}
}
In this case it does work to have both of these queries like this and my table functions correctly. Why is it that when I change "SELECT * FROM medley_relay;" to "SELECT * FROM medley_relay WHERE name = ?;" it stops working?
3
Answers
You want to preserve the row number from the original ordering of the entire table, even when you’re only selecting a single row. In that case, you can use a derived table (a subquery in the FROM clause) to calculate the row numbers over the entire table first and then perform the name filtering on the result of that derived table.
the logic you describe may like folllow:
Assuming time has an index, and you are only expecting to get a small subset of the table rows, you can most easily calculate a rank like:
(Have a time index on the table to be efficient.)
This produces an equivalent to rank(), not row_number() – ties will get the same rank. If you want row_number instead and have a primary key pkid, do
where (mr2.time,mr2.pkid) < (mr.timr,mr.pkid)
. For the equivalent of dense_rank(), change tocount(distinct mr2.time)
.