I have to perform nested queries to return values from the database for a chart. I have to do a total of 12 queries, in which each query adds up the number of customers in each month of the year.
If I use this mode, I get the right result, but written in a horrible way because I do so many lines of code:
sql = "SELECT SUM(customer) as January FROM visit WHERE date >= '2020-01-01 00:00:00' AND date <= '2020-01-31 23:59:59' AND id='4' ";
db.query(sql, function(error, result, result_fields) {
if (error) {
res.render('loginform.ejs', { message: err_message, level: "error" } );
winston.error(error);
} else {
//SAVE DATA
sql = "SELECT SUM(customer) as february FROM visit WHERE date >= '2020-02-01 00:00:00' AND date <= '2020-02-28 23:59:59' AND id='4'";
//FEBRUARY
db.query(sql, function(error, result, result_fields) {
if (error) {
res.render(...);
winston.error(error);
} else {
req.session.february_customers = ((result[0].february == null)) ? 0 : result[0].February;
...
//This is followed by another 10 queries for the other 10 months of the year.
As you can see there are many lines of code !!
Is there any way to optimize? For example using a for loop?
You should to use single query that returns yearly data grouped by month:
SELECT
DATE_FORMAT(date, '%M') AS Month,
SUM(customer) as Visits
FROM visit
WHERE
date BETWEEN '2020-01-01 00:00:00' AND '2020-12-31 23:59:59'
AND id=4
GROUP BY MONTH(date);
Related
I'm trying to get a month's worth of data using KnexJS, I only have the year and month in the form 20xx-mm.
Here's my Knex query ..
export function getMonthlyData(yearAndMonth, startingFromDay, roomId) {
return db('events')
.where('roomId', roomId)
.whereBetween('start', [`${yearAndMonth}-${startingFromDay} 00:00:00+00`, `${yearAndMonth}-31 23:59:59+00`])
.select();
}
The issue is, I'm starting at a specific date and going through to the 31'st day, some months have 30 days, some 28 and 29. How do I go about to creating a query that helps me achieve this?
I have tried using SQL's MONTH function along with Knex's betweenRaw, but unfortunately, I have only the year and month, and the MONTH function expects a datetime.
This should solve it
export function getMonthlyData(yearAndMonth, startingFromDay, roomId) {
const startDate = `${yearAndMonth}-${startingFromDay}`;
return db('events')
.where('roomId', roomId)
.whereRaw(`start >= (TIMESTAMP '${startDate}')::DATE`)
.whereRaw(`start < date_trunc('MONTH', TIMESTAMP '${startDate}' + interval '1 month')::DATE`)
.select();
}
I have two dates:
(Hour:Minute/Day)
Both in string format.
Example 1:
Main date: "00:10/01" (01.03.2020 - data available)
Secondary date: "23:45/29" (29.02.2020 - not available (should be calculated))
var mainDate = moment("01.03.2020 00:10");
var secondaryDate = "23:45/29";
Output should be: 29.02.2020 - 23:45
Information I have
Main date: 01.03.2020 [Date Object / String] (Day, Month, Year, Hour Minute)
Secondary date: "23:45/29" [String] (Hour, Minute, Day)
Output expected
It should calculate the complete date, relative to the main day
29.02.2020
Real Life Example 1
Scheduled time of departure: 23:45/01 (my system knows that /01 is the 01.01.2020)
Actual time of departure: 00:50/02 (my system doesn't know the date)
Output should be that the aircraft departure time (timestamp) was 02.01.2020 - 00:50
Real Life Example 2
Scheduled time of departure: 00:45/01 (my system knows that /01 is the 01.01.2020)
Actual time of departure: 23:50/31 (my system doesn't know the date)
Output should be that the aircraft departure time (timestamp) was 31.12.2019 - 23:50
What could be a function to calculate this?
NodeJS
MomentJS
If you get the date part of the secondary data, you can compare it to the date part of the main date. If the former is in effect before the latter, you know to subtract a month when creating the full secondary date.
function getSecondaryDate(mainDate, secondaryInfo) {
var baseDate = moment(mainDate, "DD.MM.YYYY HH:mm");
var secParts = secondaryInfo.split("/");
if (secParts.length != 2) {
// something is wrong
alert("Wrong number of parts for secondary date.");
}
var secDayOfMonth = parseInt(secParts[1]);
if (isNaN(secDayOfMonth)) {
// something went wrong
alert("Could not parse date part of secondary date '" + secDayOfMonth + "'");
}
var monthInc = 0;
if (secDayOfMonth > baseDate.date() + 15) {
monthInc = -1;
} else if (secDayOfMonth < baseDate.date() - 15) {
monthInc = 1
}
var secondaryDate = moment().year(baseDate.year()).month(baseDate.month()).add(monthInc, "month").date(secDayOfMonth);
if (!secondaryDate.isValid()) {
// something went wrong
alert("Failed to construct secondary date.");
}
return secondaryDate;
}
document.addEventListener('DOMContentLoaded', (event) => {
var mainDate = "01.03.2020 00:10";
var secondary = "23:45/29";
document.getElementById("result").innerHTML = getSecondaryDate(mainDate, secondary).format("YYYY-MM-DD") + "<br>" +
getSecondaryDate("01.01.2020 00:10", "23:50/31").format("YYYY-MM-DD");
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
<body>
<p id="result"></p>
</body>
I'm sure you can see how to add the time on too from var secTime = secParts[0].split(":");.
I have created a discord bot complete with command handlers. I am trying to make a command which takes args[1] of the user message and inputs this into a MySQL field.
In this case it would be >birthdayadd #user MM/DD/YYYY. the current code takes the args[1] if it's equal to the length 10 and adds it into the MySQL.
The current problem with this means its not formatted correctly in the mySQL and I can't create a command which sorts and returns the birthdays within certain time span.
How would I convert it so that the args[1] are converted to a date string (taking only the day and month) then inputs into the mySQL column formatted as DATE?
Code below:
const Discord = require("discord.js");
module.exports.run = async (bot, message, args, con) => {
let mention = message.mentions.users.first() || bot.users.get(args[0]);
if (!mention) return message.reply("You must mention someone or give their ID!");
con.query(`SELECT bday FROM alltime WHERE user = '${mention.id}' AND guild = '${message.guild.id}'`, function (error, results, fields) {
if (error) throw error;
if (!args[1]) return message.reply("You must specify a birthdate to add in the format DD/MM");
if (args[1].length < 10) return message.reply("format must be DD-MM-YYYY");
if (args[1].length > 10) return message.reply("format must be DD-MM-YYYY");
if (args[1].length === 10) {
con.query(`UPDATE alltime SET bday = '${args[1]}' WHERE user = '${mention.id}' AND guild = '${message.guild.id}'`);
message.channel.send(`Birthday set to ${args[1]} for ${bot.users.get(mention.id).username}`);
};
})
}
module.exports.help = {
name: "birthdayadd",
usage: "``prefix`` #member DD/MM",
description: "Add a users birthday into the database",
}
bot = discord client
con = MySQL connection details
Any help with this would be appreciated.
I recommend you use moment.js for formatting and MySQL uses YYYY-MM-DD
MySQL retrieves and displays DATE values in 'YYYY-MM-DD' format. (https://dev.mysql.com/doc/en/datetime.html)
You can use moment to
Take in the user's date string - This accepts multiple different types of dates
Format it to YYYY-MM-DD
var date = moment("March 17 2004");
console.log(date.format("YYYY-MM-DD"));
//2004-03-17
For getting only the month and day, refer to Compare only day and month with date field in mysql
Currently I have a timestamp field with value format like 1479664146607.
What I wanted to do is to get all data with timestamp that has a year of let's say 2017.
My current code is non-performant. It gets all the data, and then uses a filter method.
Let's say I got 2000+ records.
const records = []; // all records
const data = records.filter(r => new Date(r).getYear == '2017');
While this code works, it kills the server.
My database is nedb and using feathersjs, I can actually get equality items by
app.service('messages').find({
query: {
timestamp: '2017'
}
});
This code will not work because it will search for the exact year. I am looking for a way to convert the timestamp field to a year before searching it in the database.
Okay, so what I did is to use the $gt and $lt operators.
Let's say we want to get all data in year 2018.
Using momentjs, I did something like this:
const year = '2018';
// Get previous year based on given year
const previousYear = moment(year, 'YYYY').subtract(1, 'year').format('YYYY');
// Get next year based on given year
const nextYear = moment(year, 'YYYY').add(1, 'year').format('YYYY');
// get full ending date of previous year
const endOfPreviousYear = moment(previousYear, 'YYYY').endOf('year').format('x');
// get full starting date of next year
const startOfNextYear = moment(nextYear, 'YYYY').startOf('year').format('x');
// get data where year is greater than `endOfPreviousYear` and less than `startOfNextYear`
const yearQuery = {
$gt: +endOfPreviousYear,
$lt: +startOfNextYear
}
app.service('messages').find({
query: {
timestamp: yearQuery
}
});
I am using node.js and monodb database where I want to query records based on date and I want to ignore time and date only month and year will be matched here is my code:-
collection.find({ date: { "$gte": sDate, "$lt": eDate) } }).count(function (e, c) {
});
this is working but matching date and time as well how I can match only month and year? Please help me to solve this problem.
Edit:- some data from collection:-
{
"_id" : ObjectId("5535e76f82a964d011e34fcf"),
"VisitorId" : "5535e72a82a964d011e34fcb",
"date" : ISODate("2015-01-21T06:00:15.761Z"),
}
{
"_id" : ObjectId("5535e75f82a964d011e34fcf"),
"VisitorId" : "5535e72a82a964d011e34fcb",
"date" : ISODate("2015-04-21T06:00:15.761Z"),
}
I will pass two params i.e {month:"1",year:"2015"};
and in output first docs should be display.
Thanks
You could use the $where operator in your query:
collection.find({
"$where": function() {
return this.date.getMonth() == 0 && this.date.getFullYear() == 2015
}
}).count(function (err, data) {
// Handle err
});
However, query performance is rather compromised because using $where alone requires a table scan, it takes a global lock. You should use $where only when you can't express your query using another operator. If you must use $where , try to include at least one other standard query operator to filter the result set.
Other options are to modify your schema and store the month in its own property (if it's a common field in your queries). You are guaranteed better query performance since the field can be indexed.
The other option will be when query a specific month and year, create a query object that only looks for the start and the end of that specific month.
var sDate = new Date(2015, 0, 1);
var eDate = new Date(2015, 1, 1);
collection.find({ date: { "$gte": sDate, "$lt": eDate) } }).count(function (e, c) {
});