Laravel get all rows related to one another in the same table - sql

Here i have file_managerstable that contains information like id, uuid, name parent_id etc. some row may have some sub rows related by parent_id. now i want to get all the sub rows for a specific id. for an id there may have multilevel sub rows.
in the picture i want all the related id for 1.
Ex. if i want the related rows for id 1, i need all 2, 3, 4, 5 ids because all are related to id 1.
Relation
public function sub()
{
return $this->hasMany(FileManager::class, 'parent_id');
}
I tried few time but could not get the result...
Thanks

use relationship recursively
public function sub(){
return $this->hasMany(FileManager::class, 'parent_id');
}
public function recursiveSub(){
return $this->sub()->with('recursiveSub');
}
and then in the query
FileManager::with('recursiveSub')->find($id)

Related

Maintaining auto ranking as a column in MongoDB

I am using MongoDB as my database.
I have data which contains rank and name as columns. Now a new row can be updated with a rank different from ranks already existing or can be same.
If same then the ranks of other rows must be adjusted .
Rows having lesser rank than the to be inserted one must be incremented by one and the rows which are having ranks can remain as it it.
Feature is something like number bulleted list in MS Word type of applications. Where inserting a row in between adjust the numbering of other rows below it.
Rank 1 is the highest rank.
For e.g. there are 3 rows
Name Rank
A 1
B 2
C 3
Now i want to update a row with D as name and 2 as rank. So now after the row insert, the DB should like below
Name Rank
A 1
B 3
C 4
D 2
Probably using Database triggers i can achieve this by updating the other rows.
I have couple of questions
(a) Is there any other better way than using database trigger for achieving this kind of scenario ? Updating all the rows might be a time consuming job.
(b) Does MongoDB support database trigger natively ?
Best Regards,
Saurav
No, MongoDB, does not provide triggers (yet). Also I don't think trigger is really a great way to achieve this.
So I would just like to throw some ideas, see if it makes sense.
Approach 1
Maybe instead of disturbing those many documents, you can create a collection with only one document (Let's call the collection ranking). In that document, have an array field call ranks. Since it's an array it's already maintaining a sequence.
{
_id : "RANK",
"ranks" : ["A","B","C"]
}
Now if you want to add D to this rank at 2nd position
db.ranking.update({_id:"RANK"},{$push : {"ranks":{$each : ["D"],$position:1}}});
it would add D to index 1 which is 2nd position considering index starts at 0.
{
_id : "RANK",
"ranks" : ["A","D","B","C"]
}
But there is a catch, what if you want to change C position to 1st from 4th, you need to remove it from end and put it in the beginning, I am sure both operation can't be achieved in single update (didn't dig in the options much), so we can run two queries
db.ranking.update({_id:"RANK"},{$pull : {"ranks": "C"}});
db.ranking.update({_id:"RANK"},{$push : {"ranks":{$each : ["C"],$position:0}}});
Then it would be like
{
_id : "RANK",
"ranks" : ["C","A","D","B"]
}
maintaining the rest of sequence.
Now you would probably want to store id instead of A,B,C etc. one document can be 16MB so basically this ranks array can store more than 1.3 million entries of id, if id is MongoDB ObjectId of 12 bytes each. if that is not enough, we still have option to have followup document(s) with further ranking.
Approach 2
you can also, instead of having rank as number, just have two field like followedBy and precededBy.
so your user document would look
{
_id:"A"
"followedBy":"B",
}
{
_id:"B"
"followedBy":"C",
"precededBy":"A"
}
{
_id:"c"
"precededBy":"B",
}
if you want to add D at second position, then you need to change the current 2nd position and you need to insert the new One, so it would be change in only two document
{
_id:"A"
"followedBy":"B",
}
{
_id:"B"
"followedBy":"C",
"precededBy":"D" //changed from A to D
}
{
_id:"c"
"precededBy":"B",
}
{
_id:"D"
"followedBy":"B",
"precededBy":"A"
}
The downside of this approach is that you cannot sort in query based on the ranking until and unless you get all these in application and create a linkedlist sort of structure.
This approach just preserve the ranking with minimum DB changes.

Rails: Need to scope by max version

I have this problem, I've got database table that looks like this:
"63";"CLINICAL...";"Please...";Blah...;"2014-09-23 13:15:59";37;8
"64";"CLINICAL...";"Please...";Blah...;"2014-09-23 13:22:51";37;9
The values that matter are the second to last and last one.
As you can see, the second to last (abstract_category_numbers) are the same, but the last differs (version_numbers)
Here is the problem:
When I make a scope, it returns all of the records, which i need to focus on the one with the maximum version number.
In SQL i would do something like this:
'SELECT * FROM Category c WHERE
NOT EXISTS SELECT * FROM Category c1
WHERE c.version_number < c1.version_number
AND c.abstract_category_id = c1.abstract_category_id'
But i'm totally lost at Ruby, more specifically how to do this kind of select in the scope (I understand it should be a relation)
Thanks
We can create a scope to select the category with max version_number like this:
scope :with_max_version_number, -> {
joins("JOIN ( SELECT abstract_category_id, max(version_number) AS max_version
FROM categories
GROUP BY abstract_category_id
) AS temp
ON temp.abstract_category_id = categories.abstract_category_id
AND temp.max_version = categories.version_number"
)
}
Basically, we will select the category with the max_version value on temp table in the subquery.
Btw, I expect the table name is categories, you may correct it. Then the final query will be:
Category.with_max_version_number
Scopes are suppose to return an array of values even if there is only 1 record.
If you want to ALWAYS return 1 value, use a static method instead.
def self.max_abstract_category
<your_scope>.max_by{ |obj| obj.version_number }
end
If I understand your question: you have a database table with a version_number column, which rails represents using an Active Record model--that I'll call Category because I don't know what you've called it--and you want to find the single Category record with the largest version_number?
Category.all.order(version_numbers: :DESC).limit(1).first
This query asks for all Category records ordered by version_number from highest to lowest and limits the request to one record (the first record, a.k.a the highest). Because the result of this request is an array containing one record, we call .first on the request to simply return the record.
As far as I'm aware, a scope is simply a named query (I don't actually use scopes). I think you can save this query as a scope by adding the following to your Category model. This rails guide explains more about Scopes.
scope :highest_version, -> { all.order(version_numbers: :DESC).limit(1).first }
I join implementation with baby_squeel but for some reason it was very slow on mysql. So I ended up with something like:
scope :only_latest, -> do
where(%{
NOT EXISTS (SELECT * FROM Category c
WHERE categories.version_number < version_number
AND categories.abstract_category_id = abstract_category_id')
}
end
I filed a BabySqueel bug as I spent a long time trying to do in a code proper way to no avail.

Linq to SQL select one of every different result

Let's say I have a table with two columns, name and template. Let's say i have multiple rows with the name foo and multiple rows with the name bar. All of them have template 3.
How do i perform a linq to SQL select that returns only one of each name with template number 3.
(from f in g_lisFilters where f.Template == "3" orderby f.Sortering select f).ToList();
above code returns a list of all items with template number 3. How do i select only one of every name?
Thanks in advance
You simply have to group the data by template, and take the first element of each group.
var firsts = from e in g_lisFilters
where f.Template == "3"
group by e.Sortering
into groups
select groups.First();
Or
var firsts = g_lisFilters
.Where(e.Template="3")
.GroupBy(e=>e.Sortering ,(key,g)=>g.First());
Have you tried using the 'Distinct' method? It ignores multiple results of the same.
Maybe this could help you:
http://msdn.microsoft.com/en-us/library/vstudio/bb348436(v=vs.100).aspx

Sql statement with multi ANDs querying the same column

I don't know if the title of the post is the appropriate. I have the following table
and an Array in php with some items, parsed_array. What I want to do is to find all the SupermarketIDs which have all the items of the parsed_array.
For example, if parsed_array contains [111,121,131] I want the result to be 21 which is the ID of the Supermarket that contains all these items.
I tried to do it like that:
$this->db->select('SupermarketID');
$this->db->from('productinsupermarket');
for ($i=0; $i<sizeof($parsed_array); $i++)
{
$this->db->where('ItemID', $parsed_array[$i]);
}
$query = $this->db->get();
return $query->result_array();
If there is only one item in the parsed_array the result is correct because the above is equal to
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID=parsed_array[0];
but if there are more than one items, lets say two, is equal to
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID=parsed_array[0]
AND ItemID=parsed_array[1];
which of course return an empty table. Any idea how can this be solved?
There are at least two ways of generating the result you want, either a self join (no fun to generate with a dynamic number of items) or using IN, GROUP BY and HAVING.
I can't really tell you how to generate it using CodeIgniter, I assume you're better at that than I am :)
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID IN (111,121,131) -- The 3 item id's you're looking for
GROUP BY SupermarketID
HAVING COUNT(ItemId) = 3; -- All 3 must match
An SQLfiddle to test with.
EDIT: As #ypercube mentions below, if the ItemId can show up more than once for a SupermarketID, you'll want to use COUNT(DISTINCT ItemId) to count only unique rows instead of counting every occurrence.
You can use where_in in codeigniter as below,
if(count($parsed_array) > 0)
{
$this->db->where_in('ItemID', $parsed_array);
}
Active record class in codeigniter
Try an IN clause or multiple ORs:
SELECT SupermarketID
FROM productinsupermarket
WHERE ItemID=parsed_array[0]
OR ItemID=parsed_array[1];

search within an array with a condition

I have two array I'm trying to compare at many levels. Both have the same structure with 3 "columns.
The first column contains the polygon's ID, the second a area type, and the third, the percentage of each area type for a polygone.
So, for many rows, it will compare, for example, ID : 1 Type : aaa % : 100
But for some elements, I have many rows for the same ID. For example, I'll have ID 2, Type aaa, 25% --- ID 2, type bbb, 25% --- ID 2, type ccc, 50%. And in the second array, I'll have ID 2, Type aaa, 25% --- ID 2, type bbb, 10% --- ID 2, type eee, 38% --- ID 2, type fff, 27%.
here's a visual example..
So, my function has to compare these two array and send me an email if there are differences.
(I wont show you the real code because there are 811 lines). The first "if" condition is
if array1.id = array2.id Then
if array1.type = array2.type Then
if array1.percent = array2.percent Then
zone_verification = True
Else
zone_verification = False
The probleme is because there are more than 50 000 rows in each array. So when I run the function, for each "array1.id", the function search through 50 000 rows in array2. 50 000 searchs for 50 000 rows.. it's pretty long to run!
I'm looking for something to get it running faster. How could I get my search more specific. Example : I have many id "2" in the array1. If there are many id "2" in the array2, find it, and push all the array2.id = 3 in a "sub array" or something like that, and search in these specific rows. So I'll have just X rows in array1 to compare with X rows in array 2, not with 50 000. and when each "id 2" in array1 is done, do the same thing for "id 4".. and for "id 5"...
Hope it's clear. it's almost the first time I use VB.net, and I have this big function to get running.
Thanks
EDIT
Here's what I wanna do.
I have two different layers in a geospatial database. Both layers have the same structure. They are a "spatial join" of the land parcels (55 000), and the land use layer. The first layer is the current one, and the second layer is the next one we'll use after 2015.
So I have, for each "land parcel" the percentage of each land use. So, for a "land parcel" (ID 7580-80-2532, I can have 50% of farming use (TYPE FAR-23), and 50% of residantial use (RES-112). In the first array, I'll have 2 rows with the same ID (7580-80-2532), but each one will have a different type (FAR-23, RES-112) and a different %.
In the second layer, the same the municipal zoning (land use) has changed. So the same "land parcel" will now be 40% of residential use (RES-112), 20% of commercial (COM-54) and 40% of a new farming use (FAR-33).
So, I wanna know if there are some differences. Some land parcels will be exactly the same. Some parcels will keep the same land use, but not the same percentage of each. But for some land parcel, there will be more or less land use types with different percentage of each.
I want this script to compare these two layers and send me an email when there are differences between these two layers for the same land parcel ID.
The script is already working, but it takes too much time.
The probleme is, I think, the script go through all array2 for each row in array 1.
What I want is when there are more than 1 rows with the same ID in array1, take only this ID in both arrays.
Maybe if I order them by IDs, I could write a condition. kind of "when you find what you're looking for, stop searching when you'll find a different value?
It's hard to explain it clearly because I've been using VB since last week.. And english isn't my first language! ;)
If you just want to find out if there are any differences between the first and second array, you could do:
Dim diff = New HashSet(of Polygon)(array1)
diff.SymmetricExceptWith(array2)
diff will contain any Polygon which is unique to array1 or array2. If you want to do other types of comparisons, maybe you should explain what you're trying to do exactly.
UPDATE:
You could use grouping and lookups like this:
'Create lookup with first array, for fast access by ID
Dim lookupByID = array1.ToLookup(Function(p) p.id)
'Loop through each group of items with same ID in array2
For Each secondArrayValues in array2.GroupBy(Function(p) p.id)
Dim currentID As Integer = secondArrayValues.Key 'Current ID is the grouping key
'Retrieve values with same ID in array1
'Use a hashset to easily compare for equality
Dim firstArrayValues As New HashSet(of Polygon)(lookupByID(currentID))
'Check for differences between the two sets of data, for this ID
If Not firstArrayValues.SetEquals(secondArrayValues) Then
'Data has changed, do something
Console.WriteLine("Differences for ID " & currentID)
End If
Next
I am answering this question based on the first part that you wrote (that is without the EDIT section). The correct answer should explain a good algorithm but I am suggesting you to use DB capabilities because they have optimized many queries for these purpose.
Put all the records in DB two tables - O(n) time ... If the records are static you dont need to perform this step every time.
Table 1
id type percent
Table 2
id type percent
Then use the DB query, some thing like this
select count(*) from table1 t1, table2 t2 where t1.id!=t2.id and t1.type!=t2.type
(you can use some better queries, what I am trying to say is give the control to DB to perform this operation)
retrieve the result in your code and perform the necessary operation.
EDIT
1) You can sort them in O(n logn) time based on ID + type + Percent and then perform binary search.
2) Store the first record in hash map with appropriate key - could be ID only or ID+type
this will take O(n) time and searching ,if key is correct, will take constant time.
You need to define a structure to store this data. We'll store all the data in a LandParcel class, which will have a HashSet<ParcelData>
public class ParcelData
{
public ParcelType Type { get; set; } // This can be an enum, string, etc.
public int Percent { get; set; }
// Redefine Equals and GetHashCode conveniently
}
public class LandParcel
{
public ID Id { get; set; } // Whatever the type of the ID is...
public HashSet<ParcelData> Data { get; set; }
}
Now you have to build your data structure, with something like this:
Dictionary<ID, LandParcel> data1 = new ....
foreach (var item in array1)
{
LandParcel p;
if (!data1.TryGetValue(item.id, out p)
data1[item.id] = p = new LandParcel(id);
// Can this data be repeated?
p.Data.Add(new ParcelData(item.type, item.percent));
}
You do the same with a data2 dictionary for the second array. Now you iterate for all items in data1 and compare them with the item with the same id for data2.
foreach (var parcel2 in data2.Values)
{
var parcel1 = data1[parcel2.ID]; // Beware with exceptions here !!!
if (!parcel1.Data.SetEquals(parcel2.Data))
// You have different parcels
}
(Now that I look at it, we are practically doing a small database query here, kind of smelly code ...)
Sorry for the C# code since I don't really feel so comfortable with VB, but it should be fairly straightforward.