Cakephp 3 Sum 2 columns of same table - php

How to Sum two columns of the same table in CakePhp 3 ?
lets say i want to sum the values of two columns i.e stitching_amount and item_amount of table order_details .. So how can I do this ?
I have tried this piece of code for this but it is not working in Cakephp 3.8 ..
$query = $this->OrderDetails->find();
$query
->select([
'val' => $query
->newExpr()
->add($query->func()->sum(
new IdentifierExpression('OrderDetails.item_amount')
))
->add($query->func()->sum(
new IdentifierExpression('OrderDetails.stitching_amount')
))
->tieWith('+')
])
->where(['order_id' => $lastorderid]);
Whereas I have done this in corephp and its working perfectly, I want to do this in Cakephp3, like this
SELECT
SUM(stitching_amount+item_amount) AS Total
FROM
order_details
WHERE
id=" . $run_orders['id'];

Try this:
$query->select([
'Total' => $query->func()->sum($query->newExpr('OrderDetails.item_amount + OrderDetails.stitching_amount'))
]);

Related

CakePhp getting the number of entries of each code

I want to do a search of data and get the number of entries it has on another table
$query = $this->find();
$query->select([
'name',
'code',
'count' => "(
SELECT
COUNT(products_branches.id)
FROM
products_branches
INNER JOIN
branches
ON branches.company_id = products_branches.company_id
AND branches.code = products_branches.branch_code
WHERE
products_branches.deleted = 0
AND products_branches.carried = 1
AND products_branches.company_id = $company_id
AND products_branches.branch_code = code
)",
]);
is there a way that I could use the code fetch in the select and use at as one of the condition in the search condition of the subquery?
I want to search the number of entries each name and code has on product_branches table
What you're looking to do is possible, but you've got a few more steps to complete. You need to make sure you create an Association between the products_branches and branches table first. Then you can use the where() function to do what you want to do. Something like this:
$q = $this-find('all')
->select(['Branches.name', 'Branches.code'])
->contain(['ProductsBranches'])
->where(['ProductsBranches.deleted' => 0,
'ProductsBranches.carried' => 1,
'ProductsBranches.company_id' => $company_id,
'ProductsBranches.branch_code' => $code]);
$count = $q->all()->count();

MYSQL PHP: Find duplicates based on Address Column

I have an addresses table in my MYSQL database with the following structure:
The first column ID, is a primary, auto-increment column.
The second column Name is varchar.
The third column contains address (text), filled by user.
The forth column contains address slug, which is basically the address (Third Column) in lower case and without any special characters.
The last column contains the creation date of the record.
I wish to display all the records and highlight the possible duplicates, based on the address/address slug.
In this case, the duplicates are as follows:
Record 1 and Record 2
Record 3 and Record 6
Is there a way to partially match a string in MYSQL or PHP, to achieve the above results?
FYI: I have gone through SPHINX PHP, SQL FULLTEXT SEARCHES etc.
I have been struggling over 2 weeks, but couldn't find any optimal solution.
Any ideas, suggestions, solutions are welcome.
Since laravel was tagged initially, later removed, I thought the strategy can still help.
This is the given list:
$lists = [
[
'id' => 1,
'text' => '2693 Edgewood Road Exit',
],
[
'id' => 2,
'text' => '4408 Cost 4657 Avenue',
],
[
'id' => 3,
'text' => '2693 Mapleview Road',
],
[
'id' => 4,
'text' => '4657 Cost Edgewood Avenue',
],
[
'id' => 5,
'text' => '4408 Mapleview Drive Road',
]
];
Goal is to find repetitive/duplicate texts from each.
Since finding duplication of ONE word is not a real scenario, I thought of finding the duplication with TWO words with all the combinations possible.
$combinations = [];
foreach ($lists as $list) {
$insideCombo = [];
$insideText = explode(' ', $list['text']);
$length = count($insideText);
for ($i = 0; $i < $length; $i++) {
for ($j = $i + 1; $j < $length; $j++) {
if (isset($insideText[$j])) {
$insideCombo[] = $insideText[$i] . ' ' . $insideText[$j];
}
}
}
$combinations[$list['id']] = $insideCombo;
}
This is gonna return
// for '2693 Edgewood Road Exit'
1 => array:6 [
0 => "2693 Edgewood"
1 => "2693 Road"
2 => "2693 Exit"
3 => "Edgewood Road"
4 => "Edgewood Exit"
5 => "Road Exit"
]
Now, we loop again to compare the possible repetition. Here, we leverage Laravel's Str::containsAll()
$copyCat = [];
foreach ($lists as $list) {
foreach ($combinations as $comboKey => $combination) {
/* no need to compare the text with itself &&
* to avoid duplication of '4 to 2' if '2 to 4' is already mentioned
*/
if ($list['id'] != $comboKey && $list['id'] < $comboKey) {
foreach ($combination as $row) {
if (Str::containsAll($list['text'], explode(' ', $row))) {
$copyCat[] = $list['id'] . ' matches with ' . $comboKey . ' with "' . $row . '"';
}
}
}
}
}
Final Response of $copyCat
array:5 [
0 => "1 matches with 3 with [2693 Road]"
1 => "2 matches with 4 with [4657 Cost]"
2 => "2 matches with 4 with [4657 Avenue]"
3 => "2 matches with 4 with [Cost Avenue]"
4 => "3 matches with 5 with [Mapleview Road]"
]
Keep me posted in the comments below. Cheers!
Make an empty duplicate of the table - e.g. mytable_to_update.
Run a few queries to find out duplicates.
Start with populating the newly created table with non-duplicates. Initial query:
SELECT SUBSTRING_INDEX(Name,' ',1),COUNT(*)
FROM mytable_to_update
GROUP BY SUBSTRING_INDEX(Name,' ',1) HAVING COUNT(*) = 1;
The SUBSTRING_INDEX will capture the first string before space (' '). In the example, Sam Mcarthy will become Sam only. Then using that to group and count how many name occurrences it has. HAVING COUNT(*) = 1 will only show any name occurring once. But that might as well return nothing if there's a name like Joe and Joe John but the two are actually a different person with different addresses (since the first query only group by the first name occurring). Therefore, we need to add address comparison in the mix.
Add the same function to the Address column like this:
SELECT SUBSTRING_INDEX(Name,' ',1),
SUBSTRING_INDEX(Address,' ',1), /*we take the first string in the address*/
COUNT(*)
FROM mytable_to_update
GROUP BY SUBSTRING_INDEX(Name,' ',1),
SUBSTRING_INDEX(Address,' ',1) /*then add group by for the address*/
HAVING COUNT(*) = 1;
Similarly, we take only the first string occurrence from the address. So let's say for example there are two data that looks like this, Joe, 12 Street.. and Joe John, 12 St. .., what will happen is the query above will (given the SUBSTRING_INDEX function) take only the first string occurrence; Joe, 12 , which will return the count value as 2. That means both data (Joe, 12 Street.. and Joe John, 12 St. ..) are considered as duplicates and will not show in the query results.
Change the query to list out all non-duplicates ID to be inserted into mytable_to_update table:
INSERT INTO mytable_to_update
SELECT * FROM mytable WHERE ID IN
(SELECT GROUP_CONCAT(ID) /*replace everything else in the select with just `ID`*/
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1),
SUBSTRING_INDEX(Address,' ',1)
HAVING COUNT(*) = 1) ;
Note: I'm using GROUP_CONCAT(ID) because of incompatibility of sql_mode=only_full_group_by - if it's being set. Of course the result could be different (like '1,2' or '1,,,,,') but since we're only looking at any count=1, it shouldn't have a problem as it will only return 1 value. I've tested with ANY_VALUE it also return similar results.
Now you have all the non-duplicates inside the mytable_to_update table. the next step is to search for duplicates and insert the ones that you only want. This is merely a suggestion/assumption of what you might want and it's not 100% accurate due to the nature of the data value that we're comparing.
The query is similarly structured and changed only in a few places, for example:
SELECT GROUP_CONCAT(ID), /*add GROUP_CONCAT to list all the duplicates group by the first name & address string.*/
Name,
Address,
COUNT(*)
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1),
SUBSTRING_INDEX(Address,' ',1)
HAVING COUNT(*) > 1; /*Change '= 1' to '> 1' to get any records with more than 1 count.*/
Using GROUP_CONCAT to generate a comma separated list of ID that has possible duplicates.
Then add GROUP_CONCAT over all the columns listed with identical ORDER BY so every columns will be ordering by the same thing.
SELECT GROUP_CONCAT(ID ORDER BY ID), /*add ORDER BY*/
GROUP_CONCAT(Name ORDER BY ID),
GROUP_CONCAT(Address ORDER BY ID),
COUNT(*)
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1),
SUBSTRING_INDEX(Address,' ',1)
HAVING COUNT(*) > 1;
With this you go over the values it returned for any of the duplicates and compare it side by side. That way you can decide to omit any ID that you don't want to appear in the list by adding WHERE ID NOT IN(1,3 ...) etc.
Once you've finalized which ID you want to keep, you can do something like this:
INSERT INTO mytable_to_update
SELECT * FROM mytable WHERE ID IN
(SELECT SUBSTRING_INDEX(GROUP_CONCAT(ID ORDER BY ID),',',1)
/*assuming that you only want the first ID in the set, do SUBSTRING_INDEX to separate the first ID*/
FROM mytable
GROUP BY SUBSTRING_INDEX(Name,' ',1),
SUBSTRING_INDEX(Address,' ',1)
HAVING COUNT(*) > 1);
Now you'll have a table (mytable_to_update) that might probably have all non-duplicates. In case some of the data in the mytable_to_update are not what you want, you can just remove it or in case there are some data you think is not a duplicate, you can insert it. It's pretty much a manual process afterwards; well, even with the queries, only yourself can determine whether the processes/data are correct.
Here's a fiddle: https://www.db-fiddle.com/f/6Dfrn78mqZbGTwZs3U9Vhi/0

Elgg: Is it possible to Order an array by a separate variables order_by

So, I have been looking around and working with Elgg 1.8 for quite some time now, and while I am getting the hang of it, I am still a complete novice.
I have had this idea to sort my table which grabs certain information from elgg itself, specifically the groups and such. I want to sort everything by its tabs (name, total, difference, date) and for name and date elgg has been rather straight forward with that. However, I am not quite sure if I can do the same with the total and difference because of that. Take note of the code below.
$GroupArray = elgg_get_entities(array(
'type' => 'group',
//try to get this to use the order of the totalmembers
'order_by' => $Order . " " . $Sort,
'joins' => 'JOIN ' . $db_prefix . 'groups_entity ge',
'limit' => 10,
'pagination' => true,
'offset' => $Offset,
));
This $GroupArray captures all of the group stats which allows me to add all of the necessary information into the table, and the $Order and $Sort variables are dynamically added by clicking on the tabs in the table. Name and Date are already initialized in elgg with ge.name and e.time_created, but I need to do some trickery to get the members and total working the same way.
$TotalMembers = elgg_get_entities_from_relationship(array(
'relationship' => 'member',
'relationship_guid' => $Reports->guid,
'inverse_relationship' => true,
'type' => 'user',
'limit' => 20,
'joins' => array("JOIN {$db_prefix}users_entity u ON e.guid=u.guid"),
'order_by' => 'u.name ASC',
'count' => true,
'relationship_created_time_lower'
));
This is how I get the total amount of members for each column and group, it comes after the group array as that is what allows it to be grabbed. My issue now is trying to that the $TotalMembers 'order_by' to be put into the $Order variable, essentially sorting by the min to max total members for each group.
I have searched far and wide to no avail, and im not sure if my idea is plausible or if it even works as I think it can.
Is anyone able to give me a push in the right direction?
Well, it turns out it is possible, you just need to take apart Elgg's code and replace it with the Sql would otherwise create for you
$allQuery =
"SELECT DISTINCT e.guid, ge.name,
(SELECT count(DISTINCT e.guid)
FROM elgg_entities e
JOIN elgg_users_entity u
ON e.guid=u.guid
JOIN elgg_entity_relationships r
on r.guid_one = e.guid
WHERE (r.relationship = 'member'
AND r.time_created >=1507476424
AND r.guid_two = ge.guid)
AND ((e.type = 'user'))
AND (e.site_guid IN (1))
AND ( (1 = 1) and e.enabled='yes')) as totalMembers
FROM elgg_entities e JOIN elgg_groups_entity ge
ON e.guid=ge.guid
WHERE ((e.type = 'group'))
AND (e.site_guid IN (1))
AND ( (1 = 1) and e.enabled='yes')
ORDER BY totalMembers $Sort
LIMIT $Offset, 10";
$allData = get_data($allQuery);
This just takes the two different elgg statements which are really sql statements and joins them together so that it is now possible to search by the max avaliable for the total members query.

In MySQL how best to get a recordset with multiple children

Say I have a table that represents events, with a primary event_ID key. Data might be event_name, event_location, event_date.
Then I have a table that represents attendees, with a primary attendee_ID key. Data might be first_name, last_name, contact_number
I have a link table which uses the event_ID and attendee_ID as foreign keys.
Now I need to query this. I want to return a record set of events between two given dates. I also need to look up the attendees of each event.
I'll be using PHP to map these records to an object. What I'll be expecting is something like:
array(
// Multiple `events`
array(
'event_ID' => 1,
'event_location' => 'London',
'event_start' => '2014-11-27',
'attendees' => array(
// multiple `attendees`
array(
'attendee_ID' => 1,
'first_name' => 'John'
'last_name' => 'Smith',
'contact_number' => '0207 123 1234',
),
...
),
),
...
);
I have tried Googling this, but cannot think of how to express what I need to achieve in a search term. I guess I need to Query heirachical data.
What I don't want to do is get a record set of events, then iterate through them making more DB calls to populate the attendees, as this seems horribly inefficient.
Another idea I was think of was to get all event records, then query for the link table, collate the attendee IDs and query the attendee table for any that match the meetings and use PHP code to re-associate them with my event object I built from the recordset.
Is there a more elegant, best practice way of achieving this?
select
event . *,
CONCAT('[',
GROUP_CONCAT(CONCAT('{"name":"',
attendees.name,
'", id:"',
attendees.id,
'"}')),
']') attendees
FROM
`event`
LEFT JOIN
attendees ON attendees.event = event.id
GROUP BY event.id
Will return your results in the following format
1 event2 [{"name":"name-1-1", id:"6142"},{"name":"name-1-1", id:"2048"},{"name":"name-1-1", id:"1"},{"name":"name-1-1", id:"4095"}]
2 event3 [{"name":"name-2-2", id:"2"},{"name":"name-2-2", id:"4096"},{"name":"name-2-2", id:"6143"},{"name":"name-2-2", id:"2049"}]
3 event4 [{"name":"name-3-3", id:"6144"},{"name":"name-3-3", id:"2050"},{"name":"name-3-3", id:"3"},{"name":"name-3-3", id:"4097"}]
4 event5 [{"name":"name-4-4", id:"4"},{"name":"name-4-4", id:"4098"},{"name":"name-4-4", id:"6145"},{"name":"name-4-4", id:"2051"}]
5 event6 [{"name":"name-5-5", id:"6146"},{"name":"name-5-5", id:"2052"},{"name":"name-5-5", id:"5"},{"name":"name-5-5", id:"4099"}]
6 event7 [{"name":"name-6-6", id:"6"},{"name":"name-6-6", id:"4100"},{"name":"name-6-6", id:"6147"},{"name":"name-6-6", id:"2053"}]
The attendees result is a valid JSON and can be decoded using JSON_DECODE.
Make sure you have indexes This works in 0.047 seconds on 2000+ events and 8000+ attendees
I assume that you have a column on attendees table for specifying which event user attended. This is called attended_event_ID. By using this, you can do that with following;
<?php
$query = "SELECT e.event_ID, e.event_location, e.event_start,
a.attendee_ID, a.first_name, a.last_name, a.contact_number, a.attended_event_ID
FROM events e
LEFT JOIN attendees a ON a.attended_event_ID = e.event_ID
ORDER BY e.event_ID";
$result = $db->query($query);
while($row = $result->fetch_assoc()){
$events[$row["event_ID"]]["event_ID"] = $row["event_ID"];
$events[$row["event_ID"]]["event_location"] = $row["event_location"];
$events[$row["event_ID"]]["event_start"] = $row["event_start"];
$events[$row["event_ID"]]["attendees"][] = array(
"attendee_ID" => $row["attendee_ID"],
"first_name" => $row["first_name"],
"last_name" => $row["last_name"],
"contact_number" => $row["contact_number"]
);
}
?>

Query issue in Codeigniter

I want to random show 6 news/reviews on my front page but it shows the same content 6 times random but I will not have duplication of content. Here is the SQL query:
SELECT
anmeldelser.billed_sti ,
anmeldelser.overskrift ,
anmeldelser.indhold ,
anmeldelser.id ,
anmeldelser.godkendt
FROM
anmeldelser
LIMIT 0,6
UNION ALL
SELECT
nyheder.id ,
nyheder.billed_sti ,
nyheder.overskrift ,
nyheder.indhold ,
nyheder.godkendt
FROM nyheder
ORDER BY rand() LIMIT 0,6
showing my example with active record for simplicity,
try randomizing your offset instead of the order, while still limiting to 6
// get the total number of rows
$total_rows = $this->db->count_all_results('my_table');
// offset random point within the total rows
$offset = rand( 0 , $total_rows - 6 );
$q = $this->db->offset( $offset )->limit( 6 )->get( 'my_table' );
print_r( $q->result_array() );
//initialize query builder
$sql1=$sql2=$this->db;
$sql1->select('anmeldelser.billed_sti ,anmeldelser.overskrift ,anmeldelser.indhold ,anmeldelser.id ,anmeldelser.godkendt');
$sql1->from('anmeldelser');
$sql1->order_by('rand()');
$sql1->limit(3);
//get only sql string
$query1=$sql1->get_compiled_select();
$sql2->select('nyheder.id ,nyheder.billed_sti ,nyheder.overskrift ,nyheder.indhold ,nyheder.godkendt');
$sql2->from('nyheder');
$sql2->order_by('rand()');
$sql2->limit(3);
$query2=$sql2->get_compiled_select();
//combine two query
$query = $this->mydb->query("($query1) UNION ($query2)");
$result = $query->result();
I am assuming that you need to join a Two table by this comment of yours.
You didn't mention your foreign key so I am assuming that also.
It is also not clear that the column name of your tables are same or not.
So, I am posting an join query for your table in which I assume your foreign key and column name, so please correct that before using it.
Here is your query to join your table:
$query = $this->db
->select('an.billed_sti,an.overskrift,an.indhold,an.id,an.godkendt, ny.id as ny_id,ny.billed_sti as ny_billed_sti, ny.overskrift as ny_overskrift, ny.indhold as ny_indhold , ny.godkendt as ny_godkendt ')
->from('anmeldelser as an')
->join('nyheder as ny', 'ny.id_fk = an.id', 'left outer') // I am assuming here that the [id_fk] field is the foreign key
->limit(0, 6)
->order_by('puttablename.tablecolumn', 'asc') // Your you table name and column name by which you want to order, you can use [asc/desc] as your need
->get();
And If you want to UNION here is the solution for it.

Categories