how to make duplicate rows appear in mysql query results node.js - mysql

I am working on a card game to use on Discord that allows up to 3 copies of any one card to be used. I can add the first copy of a card to my mysql database and get the result without a problem, but as soon as I add another copy of a card that already exists in the deck, those copies do not appear in row results when the results from the first query are queried in a second query.
var sql1 = `SELECT * FROM deck_cards WHERE deckid = '${deckid}'`;
con.query(sql1, function (err, rows, fields) {
console.log(rows)
if (!err) {
var count = rows.length;
if (count) {
var arr_cardid = rows.map(i => i.cardid);
var sql2 = `SELECT cardname FROM card_info WHERE cardid in ('${arr_cardid.join("','")}') `;
console.log(sql2)
con.query(sql2, function (err, rows, fields) {
if (!err) {
var count = rows.length;
for (i = 0; i < count; i++) {
arr_cardname = []
arr_cardname.push(rows[i].cardname)
// list = arr_cardname.join(", ")
console.log(arr_cardname)
}
}
})
}
}
})
Table in mysql: https://i.gyazo.com/fb165c1f402f5234a3501e09d715b965.png
Deckid 1 is the deck I query for in the first query. Notice cardid 2 should have 3 total copies of it in the deck. So, I should see 3 copies of cardid 2 when I display the list of cards in that deck. The copies are put into the second query (https://i.gyazo.com/8b6c8426daee2611766242961883ec37.png) However, I only see 1 result for those copies in the output (list), the first one found in row 3 (index 2) of the table. The other 2 copies added at the very end of the table do not appear from the second query results. Is there a way to make the duplicate rows appear in the results of the second query? I have seen union, inner joins, count and such that would be used for a workaround, but I figured I'd ask if there is a simpler and more efficient way to make this work.
Is this an issue with the for loop or the second .join(", ") maybe?

Related

select top 10 in total sum in mysql table

I have MySQL table as bellow
id, name, postcode, address
in my, table postcode will be like this
AX12 3NB
NB76 5BQ
AX23 6NB
AX87 6CZ
I want to get top 10 Postcode in total like postcode start with AX appear 3 times
I have Laravel project and want to do in this syntax
DB::table('users')
I want to get the total number of any postcode first 2 letters and only the top 10 records.
Thanks
First, get all the records, then use PHP to sort and reduce the size of the array. The best way to do this is with a raw query, but since you stated above that you don't want to use a raw query, this is a quick way to handle it.
$users = DB::table('users')->get();
$postcodes_array = [];
foreach ($users as $user) {
$first_two = substr($user->postcode, 0, 2);
if (isset($postcodes_array[$first_two])) {
$postcodes_array[$first_two] += 1;
} else {
$postcodes_array[$first_two] = 1;
}
}
arsort($postcodes_array);
$postcodes_array = array_slice($postcodes_array, 0, 10);

Yii2 mysql how to insert a record into a table where column value already exists and should be unique?

I have a table, say 'mytable' that use a "rank" column that is unique. After having created some record where rank is successively rec A(rank=0), rec B (rank=1), rec C (rank=2), rec D (rank=3), rec E (rank=4).
I need to insert a new record that will take an existing rank, say 1, and modify the rank value of the following records accordingly.
The result being : rec A(rank=0), new rec (rank=1), rec B (rank=2), rec C (rank=3), rec D (rank=4), rec E (rank=5).
How can I do this ? Can this be solved with mysql only or should I write some important bunch of code in PHP (Yii2) ?
Assuming no rank is skipped you need to shift existing ranks before saving the new record. To do that you can use beforeSave() method of your ActiveRecord like this:
class MyModel extends \yii\db\ActiveRecord
{
public function beforeSave($insert)
{
if (!parent::beforeSave($insert)) {
return false;
}
if ($insert) { //only if we are saving new record
{
$query = self::find()
->where(['rank' => $this->rank]);
if ($query->exists()) { //check if the rank is already present in DB
//we will create the query directly because yii2
// doesn't support order by for update
$command = static::getDb()->createCommand(
"UPDATE " . static::tableName() .
" SET rank = rank + 1 WHERE rank >= :rank ORDER BY rank DESC",
[':rank' => $this->rank]
);
$command->execute();
}
}
return true;
}
// ... other content of your model ...
}
MySQL allows use of ORDER BY in UPDATE query, that will help us deal with fact that doing UPDATE on table is not atomic and the UNIQUE constraint is checked after each row is updated.
It would be more problematic if there are skipped ranks. In that case you will need to shift ranks only until you hit first skipped rank.
Another option might be creating an before insert trigger on the table that would do the rank shifting.
Note:
It might be a good idea to also implement afterDelete method to shift the ranks in oposite direction when some record is removed to avoid skipped ranks.
Resources:
\yii\db\BaseActiveRecord::beforeSave()
\yii\db\ActiveRecord::updateAllCounters() - replaced with direct update
MySQL triggers
MySQL UPDATE syntax

Node.js / SQL: Insert only new values

I have a small DB where i insert new data to each column at a different time. Because I'm only inserting one new value, the values for other columns will become NULL. But if thats the case, i want to keep the old value.
My DB looks like this:
One solution would be using coalesce() i guess, but i'm updating each column dynamically, and so the other column names are unknown.
function database_call(request) {
database.query(request, function (err, result) {
if (err) {
console.log(err);
}
});
}
subscribedItem.on("changed", function (dataValue) {
let databaseAttribute = subscribedItem.itemToMonitor.nodeId.value;
let databaseValue = dataValue.value.value;
databaseAttribute = databaseAttribute.substring(databaseAttribute.indexOf('.')+1)
databaseAttribute = databaseAttribute.replace(".", '');
databaseAttribute = databaseAttribute.replace(/"/g, '');
database_call("INSERT INTO Prozessdaten ("+databaseAttribute+") VALUES ("+databaseValue+")");
});
I've found this that implements a 'vertical' coalesce.
You should first do a query like this, using SUBSTRING_INDEX and GROUP_CONCAT to obtain the latest not-null value available in the database for each column.
SELECT
SUBSTRING_INDEX(GROUP_CONCAT(05Hz ORDER BY ID DESC SEPARATOR '##INDEX##'), '##INDEX##', 1) AS 05Hz,
SUBSTRING_INDEX(GROUP_CONCAT(5Hz ORDER BY updated_at DESC SEPARATOR '##INDEX##'), '##INDEX##', 1) AS 5Hz
FROM
table
LIMIT 1
After that, update the single value you really need to update and perform an insert specifying all the values for every column.

Prevent MySQL query from timing out in Apps Script

Our client's online shop uses Opencart, for marketing purposes they wanted to sync Opencart orders automatically with Google Sheets. I modified a script in google sheets to extract customer orders from the past year (based on https://www.transpacific-software.com/blog/opencart-to-google-docs-pull-and-sync-data-auto-through-scripting and https://gist.github.com/pradeepbheron/e6129c9fd9bc74e814d0)
The SQL query is:
SELECT order_id, firstname AS first_name, lastname AS last_name,
email, date_added AS order_date, CONCAT("£", FORMAT(total,2))
order_value, payment_address_1 AS billing_address_1,
payment_address_2 AS billing_address_2, payment_city AS
billing_city, payment_postcode AS billing_postcode,
payment_country AS billing_country, items_purchased FROM
(
SELECT order_id, firstname, lastname, email, date_added,
total, payment_address_1, payment_address_2, payment_city,
payment_postcode, payment_country
FROM ocbw_order
GROUP BY order_id
) AS A
JOIN (
SELECT order_id AS product_order_id, GROUP_CONCAT(name
SEPARATOR ", ") AS items_purchased
FROM ocbw_order_product
GROUP BY product_order_id
) AS B
ON A.order_id=B.product_order_id
WHERE date_added >= DATE_SUB(NOW(),INTERVAL 1 YEAR)
AND firstname != ''
It runs fine in phpMyAdmin but Google Script Editor generates an "Exceeded maximum execution time" error.
It looks like there are 7357 rows (exported from myPhpAdmin). Is there a better way to write the query? Also I am trying to rename the column headers but can only two works, i.e:
GROUP_CONCAT(name SEPARATOR ", ") AS items_purchased
and
CONCAT("£", FORMAT(total,2)) order_value
Any thoughts
QUICK UPDATE: Fri Nov 9 11:54:03 2018
1) As request by #ThisGuyHasTwoThumbs, here is a screenshot of the explain table result
2) I looked into the Best Practices doc mentioned by #bcperth. I tried to rewrite the google sheets script but ran into issues.
Here is the amended script.
function p1MySQLFetchData() {
// Change it as per your database credentials
var conn =
Jdbc.getConnection('jdbc:mysql://[dbHostIp]:3306/[dbName]',
'[dbUsername]', '[dbPassword]');
var stmt = conn.createStatement();
var start = new Date(); // Get script starting time
//change table name as per your database structure
var rs = stmt.executeQuery('[sqlQuery]');
// It sets the limit of the
// maximum nuber of rows in a ResultSet object
// Returns the currently active spreadsheet
var doc = SpreadsheetApp.getActiveSpreadsheet();
var cell = doc.getRange('a1');
var row = 0;
// Mysql table column name count.
var getCount = rs.getMetaData().getColumnCount();
// Create array to hold mysql data
var tempArray = [];
// get row and column count for later
var colCount = getCount;
// ATTEMPT TO GET ROW COUNT 1
//rs.last();
//var rowCount = rs.getRow();
//rs.beforeFirst(); // resets rs cursor
//Logger.log(RowCount);
// DOESN'T WORK! result => ReferenceError: "RowCount" is not
// defined. (line 28, file "Code")
// ATTEMPT TO GET ROW COUNT 2
//var rsCount = stmt.executeQuery('SELECT COUNT(*) AS rowcount FROM
//[sqlQuery]);
// It sets the limit of the maximum number of rows in a ResultSet
// object
//rsCount.next();
//var rowCount = rsCount.getString("rowcount");
//Logger.log(rowCount);
// DOESN'T WORK! result => 0
// Build TempArray using MySQL data
for (var i = 0; i < getCount; i++){
tempArray[0][i] = rs.getMetaData().getColumnName(i+1);
// DOESNT WORK => ERROR
// TypeError: Cannot set property "0.0" of undefined to "order_id".
// (line 39, file "Code")
//Logger.log(rs.getMetaData().getColumnName(i+1));
}
var row = 1;
while (rs.next()) {
for (var col = 0; col < rs.getMetaData().getColumnCount();
col++) {
tempArray[row][col] = rs.getString(col + 1);
//Logger.log(rs.getString(col + 1));
}
row++;
}
// BELOW DOESNT AS I CANT GET A ROW COUNT (SEE ABOVE)
// Fill Spreadsheet from tempArray
//for (var row = 0; row < rowCount; row++) {
//for (var col = 0; col < colCount; col++) {
//cell.offset(row, col).setValue(tempArray[row][col + 1]);
//}
// }
rs.close();
stmt.close();
conn.close();
var end = new Date(); // Get script ending time
Logger.log('Time elapsed: ' + (end.getTime() - start.getTime()));
// To generate script log. To view log click on View -> Logs.
}
But as you can see from the comments, I get loads of errors. Am not sure what to do next.
UPDATE Fri Nov 30 15:26:14 2018
In answer to #Tedinoz comment below.
PhpMyAdmin generates 6862 results and the query took 13.3074 seconds.
When I ran the script in Googles Script Editor, it took around 2 minutes 30 to complete and only pulls 6348 records (92.5%). The records stop after 3rd October 2018.
Regarding your suggestions:
1) I tried running a modified script, setting the INTERVAL to:
1 MONTH => I get no results in Google (it should be 529)
2 MONTH => I get 14 results (should be 1029)
3 MONTH => I get 299 results (should be 1669).
They all took about 4-7 second in myPhpAdmin vs 5 - 20 seconds for Google Script Editor
2) Do you mean exporting a csv from phpMyAdmin and importing to Google Sheets? Well I did that and it works fine.
Another thing I have noticed is that the order_id's in Google Sheets don't match that from phpMyAdmin. Weird.

how to query several MYSQL tables in one statement

There are 30 tables(categories) all with the same structure storing news items with a siteID field to filter on a particular client.
The client select which tables(categories) they show by setting the field visible(tinyint) field to 1 or 0.
I have the following test MYSQL which works okay. I am using Applicationcraft.com so the syntax is different than standard MYSQL but you can see the query.
function _getAllData(cObj,p){
var result = [];
console.log('started');
selectObj=cObj.select().from('schoolNews').order('newsIDDESC').where('siteID=?',p.siteID);
result[0] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolDocs').order('newsIDASC').where('siteID=?',p.siteID);
result[1] = cObj.exec(selectObj);
return result;
}
So I have an array with the results of each table in result[0] & result[1].
So I created the following to :
function _getAllData(cObj,p){
var result = [];
console.log('started');
selectObj=cObj.select().from('schoolNews').order('newsIDDESC').where('siteID=?',p.siteID).where('visible=?',1);
result[0] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolDocs').order('newsIDASC').where('siteID=?',p.siteID).where('visible=?',1);
result[1] = cObj.exec(selectObj);
selectObj=Obj.select().from('schoolNews_copy').order('newsIDDESC').where('siteID=?',p.siteID).where('visible=?',1);
result[2] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolNews_copy').order('newsIDDESC').where('siteID=?',p.siteID).where('visible=?',1);
result[3] = cObj.exec(selectObj);
selectObj=cObj.select().from('schoolNews_copy').order('newsIDDESC').where('siteID=?',p.siteID;
result[4] = cObj.exec(selectObj).where('visible=?', 1);
upto result[30].
I have populated schoolNews_copy with 1000 records and run the query from my app.
I am getting a timed out error.
Is this because.
query the same table causes the problem.
This is the wrong approach all together.
If not what is the best approach.
Is there a way to query every table in a single statement and populate the results into an array named results.
So the result I need is an example array :
result[0] has data visible set to 1
result[1] has data visible set to 1
result[2] has data visible set to 0
I have now restructured the table as you said. And using joins can get all the info I need in one query.
SELECT * FROM categories INNER JOIN allNews on allNews.catID = categories.catID WHERE categories.visible = 1 AND categories.siteID = '+p.siteID;
MrWarby.