Laravel query builder for Charts.js - sql

I have logic querying a MSSQL Server database view and I've been searching for a solutions along with trying different techniques. Of course I don't expect a full solution where this is two parts (query builder & frontend javascript/chart) but any helpful direction would greatly appreciated. Here I'm getting the count of Leads on a particular day grouped by the date created and source of the leads. I'm trying build this the Laravel way so to speak, and display in charts.js. I'm thinking I might need to make two different database calls/queries. One count and one for the group by source text.
Thank you for your help for a solutions or an helpful direction I can take.
I have already done total leads for another chart but this project has some extra sugar to it.
SELECT count([LeadID]) as numleads,[LeadSource],cast(leadcreated as date) as
`enter code here`createddate
FROM [myDatabase].[dbo].[viewAllLeads]
where companyid=001
group by cast(leadcreated as date),[LeadSource]
order by createddate
Chart.js set up:
- Top links (horizontal) the groupby info by LeadSource
- Left, y-axis - the count of the leads
- Bottom x-axis - the date lead was created

It should actually be straight forward, but you need to be aware that using DB::raw() is necessary. To begin with, here your query a little bit nicer formatted:
SELECT
[LeadSource],
CAST([leadcreated] as date) as createddate,
COUNT([LeadID]) as numleads
FROM [dbo].[viewAllLeads]
WHERE [companyid] = '001'
GROUP BY [LeadSource], CAST([leadcreated] as date)
ORDER BY [createddate]
And converted to an Eloquent query:
DB::table('viewAllLeads') // the query builder doesn't care if it's a table or view
->select([
'LeadSource',
DB::raw('CAST(leadcreated as date) as createddate'),
DB::raw('COUNT(LeadID) as numleads')
])
->where('companyid', '001')
->groupBy('LeadSource', DB::raw('CAST(leadcreated as date)'))
->orderBy('createddate')
->get();
This will return a Illuminate\Support\Collection containing stdClass objects of the following form:
object(stdClass)#1 (3) {
["LeadSource"] => string(3) "ABC"
["createddate"] => string(10) "2019-08-05"
["numleads"] => int(15)
}

$leads = Lead::where('companyid', $companyid)->get()->groupBy(function ($item) {
return $item->created_at->format('Y-m-d');
})->sortBy(function ($item) {
return $item->created_at->format('x');
});
if you use dd($leads) you can see the data structure.

Related

How to modify value in column typeorm

I have 2 tables contractPoint and contractPointHistory
ContractPointHistory
ContractPoint
I would like to get contractPoint where point will be subtracted by pointChange. For example: ContractPoint -> id: 3, point: 5
ContractPointHistory has contractPointId: 3 and pointChange: -5. So after manipulating point in contractPoint should be 0
I wrote this code, but it works just for getRawMany(), not for getMany()
const contractPoints = await getRepository(ContractPoint).createQueryBuilder('contractPoint')
.addSelect('"contractPoint".point + COALESCE((SELECT SUM(cpHistory.point_change) FROM contract_point_history AS cpHistory WHERE cpHistory.contract_point_id = contractPoint.id), 0) AS points')
.andWhere('EXTRACT(YEAR FROM contractPoint.validFrom) = :year', { year })
.andWhere('contractPoint.contractId = :contractId', { contractId })
.orderBy('contractPoint.grantedAt', OrderByDirection.Desc)
.getMany();
The method getMany can be used to select all attributes of an entity. However, if one wants to select some specific attributes of an entity then one needs to use getRawMany.
As per the documentation -
There are two types of results you can get using select query builder:
entities or raw results. Most of the time, you need to select real
entities from your database, for example, users. For this purpose, you
use getOne and getMany. But sometimes you need to select some specific
data, let's say the sum of all user photos. This data is not an
entity, it's called raw data. To get raw data, you use getRawOne and
getRawMany
From this, we can conclude that the query which you want to generate can not be made using getMany method.

How to express a 'smaller than or equals' relation in Knex.js

In my back-end I'm using KnexJS with PostgreSQL and I have to build a Knex function using its SQL builder without the raw SQL.
It is the first time for me to use KnexJS and I got few issues.
What I have to build is as shown in the SQL example
UPDATE
feed
SET
status = 'SOME_STATUS'
WHERE
created_at <= 'SOME_TIMESTAMP'
AND conversation_id = 'ID';
This SQL is updating the table feed all columns with status and where two conditions are met.
In Knex what I tried as example code of my idea
answerPendingMessages(feeds) {
return this.tx(tableName).where({
conversationId,
createdAt <= timestamp // This not idea how to do in Knex ???
}).update({
status: 'ANSWERED'
})
}
In this above function my concern is how to actually convert the where part as one of the consitions is createdAt <= 'TIMESTAMP'
I understood that I can use where with an object but cannot understand how to include the <=
Also I should update the updatedAt column with the new timestamp but also that blocked me at the moment.
Te result in the end should that all columns which met the conditions are updated status to some some status and also a new updatedAt timestamp.
I'm not sure if there is a specific way with Knex to do so
This answer is to provide my goal for my previous question.
The script I'm showing is not doing anything and I don't know know to make it to work.
answerPendingMessages(feeds) {
if (isEmpty(feeds)) return Promise.resolve([]);
const { conversationId, createdAt } = feeds;
return this.tx(tableName)
.where(
columns.conversationId === conversationId,
columns.createdAt <= createdAt
)
.update({
[columns.status]: 'ANSWERED',
[columns.updatedAt]: new Date(),
});
}
What should happen here is that to update a table where I have two conditions but one of then is 'smaller than or equals' relation.
As the output of this should be that all rows where the 2 conditions are met are update status column.
Right now the script is not failing either success piratically nothing is happening.

ZF2 how to avoid sql query limit to add quotes in subquery

I'm trying to set up a subquery in ZendFramework 2 and I got an issue with the limit function for a Select object. Whatever I do, numeric value is put between quotes and makes my query fails : I should get LIMIT 1 and instead I get LIMIT '1'.
Seems this is not the first time this issue has been encountered, I saw some have asked about this issue before (like 8 months ago) but without getting any proper answer.
I also saw this issue has been marker as resolved in 2012 (https://github.com/zendframework/zf2/pull/2775) so I really don't understand what's happening there.
Here's my code in ZF2 :
$resultSet = $this->tableGateway->select( function (Select $select) use ($params) {
$sub = new Select();
$sub->from(array('temp' => 'scores'))
->columns(array(new \Zend\Db\Sql\Expression("id AS id")))
->where(array('temp.glitch' => array('None', 'Glitch')))
->where('temp.zone=scores.zone')
->order('temp.multi DESC, temp.score DESC')
->limit(1);
$select->join('players', 'player=players.id', array('player_name' => 'name', 'player_url' => 'name_url'))
->join('countries', 'players.country=countries.id', array('country_name' => 'name', 'country_iso' => 'iso'))
->join('cars', 'car=cars.id', array('car_name' => 'name'), 'left')
->join('zones', 'zone=zones.id', array('zone_name' => 'name'));
$select->where(array('scores.id' => $sub));
$select->order('scores.zone ASC');
print_r($select->getSqlString());
});
This should render the following query (which I get right except LIMIT '1' instead of LIMIT 1) :
SELECT "scores".*, "players"."name" AS "player_name", "players"."name_url" AS "player_url", "countries"."name" AS "country_name", "countries"."iso" AS "country_iso", "cars"."name" AS "car_name", "zones"."name" AS "zone_name"
FROM "scores" INNER JOIN "players" ON "player"="players"."id"
INNER JOIN "countries" ON "players"."country"="countries"."id"
LEFT JOIN "cars" ON "car"="cars"."id"
INNER JOIN "zones" ON "zone"="zones"."id"
WHERE "scores"."id" = (SELECT id AS id FROM "scores" AS "temp" WHERE "temp"."glitch" IN ('None', 'Glitch')
AND temp.zone=scores.zone ORDER BY "temp"."multi" DESC, "temp"."score" DESC LIMIT 1)
ORDER BY "scores"."zone" ASC
Since this doesn't seem to work this way, is there another way I could proceed to get my limit (using Mysql 5 database) ?
EDIT :
Thanks for your help. Finally I figured out a way to get things done the way I want and to remove the quotes by simply remove the subquery construction and to write it directly in the where function :
$select->where('scores.id = (SELECT id FROM scores AS lookup WHERE lookup.zone = scores.zone ORDER BY multi DESC , score DESC LIMIT 1)');
Although I can continue my dev with this, I feel more like using a poor trick to get rid of this issue and so I will let this question unanswered until someone comes with a real solution there.
Anyway there might be no solution at all, since it might be an issue in ZF2 core itself.
Change the line -
$select->where(array('scores.id' => $sub));
with
$select->where(array('scores.id' => new \Zend\Db\Sql\Expression("({$sub->getSqlString($this->tableGateway->adapter->getPlatform())})"));
Try with just above change.
And if it still doesn't work then make changes to the core Select class file located at -
PROJECT_FOLDER/vendor/zendframework/zendframework/library/Zend/Db/Sql/Select.php
Line No. 921 -
Change $sql = $platform->quoteValue($limit); with $sql = $limit;
Line No. 940 -
Change return array($platform->quoteValue($offset)); with return array($offset);
I have come across the issue from github and wondered as why it is still not working with the latest ZF2 files. I know the solution given above doesn't look like the proper one but I had to somehow make it work. I have tried it and it works.
Its only a quick fix before the actual solution comes into picture.

Limit models to select

I have a database table called Event which in CakePHP has its relationships coded to like so:
var $belongsTo = array('Sport');
var $hasOne = array('Result', 'Point', 'Writeup', 'Timetable', 'Photo');
Now am doing a query and only want to pull out Sport, Point, and Timetable
Which would result in me retrieving Sports, Events, Points, and Timetable.
Reason for not pulling everything is due the results having 17000+ rows.
Is there a way to only select those tables using:
$this->Event->find('all');
I have had a look at the API but can't see how its done.
You should set recursive to -1 in your app_model and only pull the things you require. never use recursive of 2 and http://book.cakephp.org/view/1323/Containable is awesome.
just $this->Event->find('all', array('contain' => array()));
if you do the trick of recursive as -1 in app_model, this is not needed, if would just be find('all') like you have

Rails (or maybe SQL): Finding and deleting duplicate AR objects

ActiveRecord objects of the class 'Location' (representing the db-table Locations) have the attributes 'url', 'lat' (latitude) and 'lng' (longitude).
Lat-lng-combinations on this model should be unique. The problem is, that there are a lot of Location-objects in the database having duplicate lat-lng-combinations.
I need help in doing the following
Find objects that share the same
lat-lng-combination.
If the 'url' attribute of the object
isn't empty, keep this object and delete the
other duplicates. Otherwise just choose the
oldest object (by checking the attribute
'created_at') and delete the other duplicates.
As this is a one-time-operation, solutions in SQL (MySQL 5.1 compatible) are welcome too.
If it's a one time thing then I'd just do it in Ruby and not worry too much about efficiency. I haven't tested this thoroughly, check the sorting and such to make sure it'll do exactly what you want before running this on your db :)
keep = []
locations = Location.find(:all)
locations.each do |loc|
# get all Locations's with the same coords as this one
same_coords = locations.select { |l| l.lat == loc.lat and \
l.lng == loc.lng }
with_urls = same_coords.select { |l| !l.url.empty? }
# decide which list to use depending if there were any urls
same_coords = with_urls.any? ? with_urls : same_coords
# pick the best one
keep << same_coords.sort { |a,b| b.created_at <=> a.created_at }.first.id
end
# only keep unique ids
keep.uniq!
# now we just delete all the rows we didn't decide to keep
locations.each do |loc|
loc.destroy unless keep.include?( loc.id )
end
Now like I said, this is definitely poor, poor code. But sometimes just hacking out the thing that works is worth the time saved in thinking up something 'better', especially if it's just a one-off.
If you have 2 MySQL columns, you can use the CONCAT function.
SELECT * FROM table1 GROUP BY CONCAT(column_lat, column_lng)
If you need to know the total
SELECT COUNT(*) AS total FROM table1 GROUP BY CONCAT(column_lat, column_lng)
Or, you can combine both
SELECT COUNT(*) AS total, table1.* FROM table1
GROUP BY CONCAT(column_lat, column_lng)
But if you can explain more on your question, perhaps we can have more relevant answers.