SQL : Merge two rows by some ID and keep values - php

I'm facing a relatively easy problem but I can't manage to wrap my head around a solution. Keep in mind I'm using PHP with the Laravel framework if it can makes things any easier.
I have a table filled with data like so :
ID | TRANSACTION_ID | BEACON_TYPE
---+----------------+---------------
1 | 1 | "abc"
2 | 2 | "def"
3 | 2 | "xyz"
and I would like to group them by transaction ID and to keep the beacon type inside the data like so:
ID | TRANSACTION_ID | BEACON_TYPE
---+----------------+---------------
1 | 1 | "abc"
2 | 2 | "def", "xyz"
I've tried using group by with no avail. Any ideas or hints on how to accomplish that? As I said earlier there might be some way to do it with Laravel Eloquent.

Assuming you're using MySQL, the function you're looking for is GROUP_CONCAT(). Using Eloquent, it would look something like this:
$transactions = DB::table('tableName')
->select('TRANSACTION_ID', DB::raw('GROUP_CONCAT(BEACON_TYPE SEPARATOR ', ') as BEACON_TYPE'))
->groupBy('TRANSACTION_ID')
->get();
Note, if you want to change the separator, you just need to edit the ', ' to something else. The default separator is ','.

Use GROUP_CONCAT() to concatenate values of the group. Give it a SEPARATOR of ', ' to get the groups with comma separated values.
Your expected result is going to lose some information, since the IDs of the grouped row will be both 2 and 3 (in your example). So either don't select those IDs, or add them with a separate GROUP_CONCAT().
The raw SQL query would be using GROUP_CONCAT(). The following queries use the table foo, replace that with your actual table name.
SELECT TRANSACTION_ID,
GROUP_CONCAT(BEACON_TYPE SEPARATOR ', ') AS BEACON_TYPE
FROM foo
GROUP BY TRANSACTION_ID
Using Eloquent, you need to use DB::raw() to select the GROUP_CONCAT() part, as there's no Eloquent method for GROUP_CONCAT(), so then it becomes
$result = DB::table('foo')
->select('TRANSACTION_ID',
DB::raw("GROUP_CONCAT(BEACON_TYPE SEPARATOR ', '") as BEACON_TYPE)
->groupBy('TRANSACTION_ID');
or if you want to include the grouped IDs to, then..
$result = DB::table('foo')
->select('TRANSACTION_ID',
DB::raw("GROUP_CONCAT(BEACON_TYPE SEPARATOR ', '") as BEACON_TYPE),
DB::raw("GROUP_CONCAT(ID SEPARATOR ', '") as ID)
->groupBy('TRANSACTION_ID');

If you already have the data from the database and just want to use collection functions you can do the following:
$things = $things->groupBy('TRANSACTION_ID')
->map(function($x) {
$beaconTypes = $x->implode('BEACON_TYPE', ', ');
$firstThing = $x->first();
$firstThing['BEACON_TYPE'] = $beaconTypes;
return $firstThing;
});

Related

Find the matching row in laravel

Here is my Table
Id | No |Group
1 | 1 |Alpha
1 | 1,2 |Alpha
1 | 2,4,5|Alpha
How can i find the the row which has No as 5 using laravel eloquent
$Match = MyModel::whereIn('No', array(5))->get();
But it didn't return any rows.
When i try to see the query executed it shows me
select * from `table` where `No` in (5)
How can i do this in php and laravel
As a pure Eloquent workround, you might be able to do something like:
$id = 5
$Match = MyModel::with(array('No' => function($query) use($id) {
$query->where_id($id);
}))->get();
Using Raw and FIND_IN_SET, something like:
$Match = MyModel::whereRaw(
'find_in_set(?, `No`)',
[5]
)->get();
(Untested)
But this will never be an efficient query because it can't use indexes; and there are many other reasons why a comma-separated list is a bad idea (such as lack of referential integrity)
The real solution will always be to normalize your database properly

Collect values from DB, group matching values, count it and use in other code

This is what my customers_basket table looks like:
customers_id | products_id | basket_quantity
3 | 56:3121fefbe6043d6fc12e3b3de2c8fc38 | 3
3 | 56:fb4c9278fcfe6225b58c06711a7e62ef | 1
3 | 56:8e334fce09556108f5416e27154b6c27 | 1
3 | 52:f3b9f38e4ddd18035bc04cd264b0f052 | 1
This is the query I'm using:
$products_in_cart_query = "SELECT products_id FROM customers_basket WHERE customers_id = " . $_SESSION['customer_id'] ."";
$products_in_cart = $db->Execute($products_in_cart_query);
$products_in_cart_model = $products_in_cart->fields['products_id'];
$products_in_cart_model = substr($products_in_cart_model, 0, strpos($products_in_cart_model, ":"));
The end result I get is 56,56,56,52
First of all, how do I use the first line's quantity field? I'd need to list that products_id 3 times since quantity is 3. Therefore, the end result needs to be: 56,56,56,56,56,52
or, for easier understanding (56,56,56),56,56,52
And second, how do I count how many same values I have? In this case, I have 5x56 and 1x52. I need to use those counts in my further calculation.
EDIT: further calculations explained
I need to know how many of each product_id I have and then run something like this:
foreach(product_id) {
$shipping_cost += FIXED_VALUE * basket_qty;
}
To get the basket quantity, you have to select it. It would be best if the first portion of the product ID was stored in a separate column, rather than having to do messy operations like substringing.
Query 1: 2-character codes and corresponding quantities
SELECT SUBSTR(products_id, 1, 2) AS product_code, basket_quantity
FROM Customers_Basket
WHERE customers_id = 3;
Query 2: 2-character codes and summed quantities
SELECT product_code, SUM(basket_quantity) AS total_quantity
FROM (SELECT SUBSTR(products_id, 1, 2) AS product_code, basket_quantity
FROM Customers_Basket
WHERE customers_id = 3
)
GROUP BY product_code;
If you really, really, really desperately want 3 rows of data for the product ID 56:3121fefbe6043d6fc12e3b3de2c8fc38, then you have to know ways to generate rows. They're truly painful in the absence of convenient SQL support (so much so, that you'd do better to select a row in PHP with the quantity and then generate the appropriate number of rows in your array in the client-side (PHP) code). I'm going to assume that some variation on these queries will get you the information you want.

SEARCH PHP and MYSQL

I need mysql code or php to handle some search query
Lets say we have these 5 items in our store.
ID | TYPE | Pattern
1. | Kilner | scissor
2. | Kilner | forcep
3. | Boyd | scissor Small
4. | Boyd | scissor large
5. | Boyd | forcep
6. | Boyd | clamp
Could you help me mysql query to handle below operation
If we search 'boyd' then numbers 3 4 5 and 6 should come up.
If we search 'scissor' then numbers 1 3 and 4 should come up.
If we search 'boyd scissor', numbers 3 and 4 should come up.
If they search' Kilner scissor' then only no 1 should display.
Let me know
thanks
the way to do it in mysql is full text search
SELECT *, MATCH(field) AGAINST ('word1 word2 word3') AS score
FROM table
WHERE MATCH(field) AGAINST('word1 word2 word3')
look this tutorial http://devzone.zend.com/26/using-mysql-full-text-searching/
try this query
SELECT * FROM `table` WHERE `name` LIKE '%$search_var%'
PDO structure
$db = $this->pdo->prepare("SELECT * FROM `table` WHERE `name` LIKE :mysearch");
$db->execute( array("mysearch"=>'%'.$mysearch.'%') );
The query you ask is a bit complex. You want to return matches in both columns, but if both columns match, then single matches have to be discarded.
This means, one way or another, run a query requiring two matches and one requiring one match, comparing the results and returning the appropriate set.
Performance-wise, I believe it is better to run one query that will fetch both, and then handle the results in PHP (you could handle them in MySQL through the use of a superquery).
So:
// We split keywords
$keywords = array_unique(preg_split('/\s+/', $search));
$inset = array();
foreach($keywords as $keyword)
$inset[] = "'".mysql_real_escape_string($keyword)."'";
$sql_in = '('.implode(',', $inset).')';
$query = "SELECT *, IF(type IN $sql_in, 1,0)+IF(pattern IN $sql_in,1,0) AS matches FROM mytable WHERE (type IN $sql_in) OR (pattern IN $sql_in) ORDER BY matches DESC;";
The above uses the discouraged mysql_ functions. Using PDO, that would be:
$keywords = array_unique(preg_split('/\s+/', $search));
// Generate a (?,?,?..?) template as long as $keywords
$sql_in = '('.implode(',', array_fill(0, count($keywords), '?')).')';
$query = "SELECT *, IF(type IN $sql_in, 1,0)+IF(pattern IN $sql_in,1,0) AS matches FROM mytable WHERE (type IN $sql_in) OR (pattern IN $sql_in) ORDER BY matches DESC;";
$st = $db->prepare($query);
$st->execute($keywords);
Note that the above uses exact match, so "Boyd" will retrieve a match with "Boyd", but "Boy" won't. Use the % matching character to change this behaviour.
Now we retrieve a table which is identical to MyTable but has one extra column, "matches", containing either 2 or 1. Can't contain 0 because of the WHERE limitation: one of the two matches must be true and count as 1.
The 2's will be returned first, so we can do
if (!isset($matches))
$matches = $tuple['matches'];
else
if ($tuple['matches'] < $matches)
break;
that is, we save the first (and highest) value, and only accept that value for the subsequent tuples. As soon as an inferior match comes by, we exit the loop and close the cursor.
This may be done in MySQL with
SELECT * FROM ( the above query ) AS newTable
WHERE matches = (
SELECT MAX(matches) FROM ( the above query ) AS tmpTable
);
but it incurs a performance penalty.
$search=array('byod','scissor');
$st=""; $st2="";
foreach($search as $value){$st.="type=%$value% or ";$st2.="pattern=%$value% or ";}
$st2=substr($st2,0,-3);
echo "select * from tablename where $st $st2";

mysql get many id's on one row

How to get all ids separated by comma on one row.
id | name
1 | Jonny
2 | Lisa
3 | Ben
And with php/mysql get with one query without a loop the ids comma separated. Like "1,2,3"
To be something like this :
$query = "SELECT 1,2,3 as oneRowIds FROM tableName";
$result = mysql_query($query);
$result = mysql_fetch_assoc($result);
echo $result['oneRowIds'];// and that shows "1,2,3"
use group_concat function
GROUP_CONCAT() function is used to concatenate column values into a single string. It is very useful if you would otherwise perform a lookup of many row and then concatenate them on the client end.
select group_concat(id) as oneRowIds FROM tableName

Select rows where column text contains value of an array

I'm building a search function in php/mysql and I'm looking for the right MySql function. My table sort of looks like this:
id | text
--------------------------------------
1 | I like pony's.
2 | Do you like fish?
3 | We like fishes!
I want to search the column 'text' for one of the exact values of an array, for example:
$search_array = array('fish','dogs','cat','panda');
I'm looking for the right MySql function to return only the second row (with the current array). The array can contain hundreds of values.
I have 6000+ rows, growing everyday with +/- 400. I've tried REGEXP but with a large array, it took about 10 seconds before it returned the corresponding rows.
Please help, I'm fighting with this for almost 3 full days now... Thanks in advance!
If the search array is constant, or changes infrequently, I recommend having another two tables, 'tags' and 'tags-text'.
For example, the row with id 2 in your example contains fish, since fish is in our 'tags' table a new record will be placed in a 'tags-text' table. When you are searching with your array, you can search if one of the array components is in the 'tags-text' table, and join the 'text' table and return the text and id and do whatever you need.
Structure of other tables:
'tags' table
id | tags
--------------------------------------
1 | fish
2 | dogs
3 | cats
'tags-text' table
text-id | tags-id
--------------------------------------
2 | 1
Does this help/make sense
Ok I think I've found the easiest solution: let PHP create the mysql query and solve it with WHERE LIKE.
$search_array = array('fish','dogs','cat','panda');
$string = '';
foreach($search_array as $term) {
$string = $string."text LIKE '%".$term."%' AND ";
}
The result of the foreach loop is:
"text LIKE '%fish%' AND LIKE '%dogs%' AND LIKE '%cat%' AND LIKE '%panda%' AND "
Now lets remove the tail of that string and write the query:
$string = substr($string, 0, -5); // removing " AND " at the end of the string
$query = "SELECT * FROM table WHERE $string";
$results = mysql_query($query);
Thanks for the other answers anyway :)
Ok, maybe you should try mixing mysql and php a bit.
Here is the pseudo-code
select 100-1000 rows at one time from db
use strpos to check each element in your array against the text column
if element found
store it
if 2 elements found break the loop
else
continue
Something like this maybe ...
$search_term = implode(",",$search_array);
SELECT * FROM your_table WHERE text IN ($search_term)";

Categories