Create custom hierarchy MDX for dimension - mdx

In a cube there's an item dimension. An item - other than an item number - can also have other (numerical) attributes.
D_Item
Attribute [attrib1]
Element1 [70]
Element2 [40]
Element3 [1]
Element4 [2]
Other hierarchies
My goal is to use a iif or case ... when ... to create a new hierarchy (or set?) that I could use for slicing.
Why not persist it into the cube? Because it is a very single use case and it is for use in a report...
My desired structure looks like this:
WITH MyNewHierarchy AS
CASE
WHEN [D_Item].[attrib].Value = 70 then 'Company 1'
WHEN [D_Item].[attrib].Value = 40 then 'Company 2'
WHEN [D_Item].[attrib].Value = 1 OR [D_Item].[attrib].Value = 2 then 'Company 3'
END
SELECT Measure ON COLUMNS,
[D_Date].[Month].Chilren on ROWS
FROM CUBE
WHERE
[MyNewHierarchy].[Company 3]
That's probably quite a relational approach but I hope to reach this result one day.

I don't know if you can make a whole new hierarchy on the fly.
I know you can create custom members and "host" them in an unused existing hierarchy. You can do this with tuples but you must include the [All] member of the host in each tuple.
So your three new members would be like so:
MEMBER [SomeExistingDim].[SomeExistingHier].[All].[Company 1] AS
(
[SomeExistingDim].[SomeExistingHier].[All]
,[D_Item].[attrib].[Element1]
)
MEMBER [SomeExistingDim].[SomeExistingHier].[All].[Company 2] AS
(
[SomeExistingDim].[SomeExistingHier].[All]
,[D_Item].[attrib].[Element2]
)
MEMBER [SomeExistingDim].[SomeExistingHier].[All].[Company 3] AS
(
[SomeExistingDim].[SomeExistingHier].[All]
,[D_Item].[attrib].[Element3]
)
+
(
[SomeExistingDim].[SomeExistingHier].[All]
,[D_Item].[attrib].[Element4]
)
Then you can create a custom set from just the above:
SET [newMembers] AS
{
[SomeExistingDim].[SomeExistingHier].[All].[Company 1],
[SomeExistingDim].[SomeExistingHier].[All].[Company 2],
[SomeExistingDim].[SomeExistingHier].[All].[Company 3],
}

Related

How to get id's from a list that are NOT in a table?

I am receiving a list of ID's. Most of these already exist in a table. I need to find which ID's are NOT in the table. This question has nothing to do with joins.
My API will receive a list of IDs, such as: [1, 2, 3, 4, 5]
Let's say there are three records in the table: [2, 3, 4]
The result I'm looking for is the array: [1, 5]
Our SQL brains jump quickly to something like the following, but clearly that's not what we need:
select * from widgets where id not in [list]
We don't need the records not in the list, we need the part of the list not in the records!
My fallback is to retrieve all records in the list and subtract from the list, something like this:
existing_ids = Widget.where(id: id_list).pluck(:id)
new_ids = id_list - existing_ids
That will work...but feels heavy-handed. Particularly if id_list has 100,000 records, and the table has 99,999 of those records.
I've searched around, and the only similar result is ID from list that is not in a table ... which did not find a viable solution.
Is there any way to do this in a single SQL query? (Bonus points for an ActiveRecord solution!)
To compare the lists to each other, either the input list needs to go into the database or the list of existing ids needs to come out of the database. The latter you already tried and didn't like, so here's an alternative
SELECT "id" FROM unnest('{1,2,3,4,5}'::integer[]) AS "id" WHERE "id" NOT IN (SELECT "id" FROM "widgets");
Not sure about performance.
Depending how many records are in your database, the simplest thing might just be to select all of the IDs and then drop the duplicates in Ruby.
from_api = [1,2,3,4,5]
existing = Widgets.pluck(:id) # => [2,3,4]
from_api.difference(existing) # => [1,5]
Obviously, if you have a substantial dataset, this will be less than optimal.
This should work.
from_api = [1,2,3,4,5]
existing = Widgets.order(:id).ids # => [2,3,4]
new_ids = []
from_api.each{ |n| new_ids << n unless existing.include? n }
new_ids # => [1,5]
or
from_api = [1,2,3,4,5]
existing = Widgets.order(:id).ids # => [2,3,4]
from_api.map{ |n| n == existing.first ? (nil if existing = existing.drop(1)) : n }.compac # => [1,5]
Balancing the complexity (to the current and future developers) of unset approach, I decided for my project that the simpler approach was warranted. While I didn't profile performance, I believe any gains would be minimal, if any.
Here is the solution I ended up with:
class Widget < ApplicationRecord
def self.absent(names)
uniq_names = names.uniq
uniq_names - where(name: uniq_names).pluck(:name)
end
end
And tests:
describe '.absent' do
subject { described_class.absent(names) }
let!(:widget1) { create(:widget, name: 'old-1') }
let!(:widget2) { create(:widget, name: 'old-2') }
let(:names) { %w[new-2 old-2 new-1 old-1 new-1 old-1] }
it { is_expected.to eq %w[new-2 new-1] }
end

Group by query, each group has to not have any item not in a List

In need a query that will help me solve this.
Here's my table 'tags':
id (int)
name (String)
user_id (int)
hardware_id (int)
I am grouping the results of the 'tags' table by hardware_id. I also have a List of tags (List<string>).
I want to get the hardware Id of the groups that all of the tags in the custom List matches at a name in the table above.
In other words, I want to get the hardware_id's that the custom List tags matches their name's. There might be name's that doesn't have a match in the custom list, but all of the custom list tags, must be in the group, and if it satisfies this need, I can the Id of that group.
I found it hard to explain and I didn't get an answer for that. I thought about doing it with foreach because it was so hard to solve, but I couldn't do it either and it's very inefficient to do it that way.
Example:
List : ['tag1', 'tag2']
Table Rows:
1, tag1, 5, 1
2, tag2, 5, 1
3, tag3, 5, 1
4, tag4, 5, 2
5, tag5, 6, 2
In this case, I should get the hardware_id of 1, because although one of the hardware Id's have tag3, it doesn't have any rows with a tag name that isn't in the List. IF the List had 'tag4', the hardware_id = 1 WOULDN'T be returned, because the List has a tag that the hardware_id group doesn't have.
If the Group doesn't have an item that the List has, it won't appear in the final result.
Someone game me this code, but it didn't work:
List<decimal> matchingHardareIds = db.tags.GroupBy(x => x.hardware_id)
.Where(x => x.All(s => tags.Contains(s.name.ToLower()) || 0 == tags.Count() && (s.user_id == userId)))
.Select(x => x.Key).ToList();
In that query, when I have one tag in the List and in the table I have several items with hardware_id 1 and one of them has a 'name' that is equal to the value in the List it will return empty results. this is because the rows in the table for a specific group by hardware_id, has a row with a name that doesn't appear in the custom List.
I need a query in either Entity Framework or Linq. Thanks a lot.
Use this:
var t = db.tags.GroupBy(x => x.hardware_Id)
.Where(x => tags.All(y =>
x.Any(z=> z.name == y)))
.Select(x=>x.Key).ToList();
Can not provide you with the entity framework or linq query, but the sql solution is to count the matches like this:
SELECT hardware_id
FROM tags
WHERE name IN (<list>)
GROUP BY hardware_id
HAVING COUNT(DISTINCT name) = <listDistinctCount>
<listDistinctCount> is the count of distinct values in the list. Which you can prepare prior to the query.

Merge the results of two active record queries

I want to merge the results from the following queries:
Dispenser.includes(:reviews).where.not(reviews: { id: nil })
and
Dispenser.includes(:dispenser_reviews).where.not(dispenser_reviews: { id: nil })
I have tried simply placing a || in the middle of these two queries, but that does not give the expected result. I want to find all Dispensers with a review or a dispenser_review..
So let's say I have the following dispenser ids from each query:
[1, 2, 3] and [2, 3, 4] ..
The output should be the dispensers represented by the ids [1, 2, 3, 4]
You can accomplish that using https://github.com/activerecord-hackery/squeel rather than active record. It provides the more advanced functionality that Arel does not have out of the box.
That being said, your logic is gonna be pretty nasty. If you wanted to get the result set and you didn't mind two queries instead of one, I'd just join the two results with the + operator.
r1 = Dispenser.includes(:reviews)# ...
r2 = Dispenser.includes(:dispenser_reviews)# ...
result = r1 + r2
As for a squeel example, it'd be something like:
Dispenser.includes{reviews}.
includes{dispenser_reviews}.
where{(reviews.id.not_eq nil) | {dispenser_reviews.id.not_eq nil)}.
references(:all)
Joins will do an INNER JOIN and only return dispenser objects that have reviews or dispenser_reviews. Pipe '|' will get rid of dups.
Dispenser.joins(:reviews) | Dispenser.joins(:dispenser_reviews)
or to get ids
Dispenser.joins(:reviews).pluck(:id) | Dispenser.joins(:dispenser_reviews).pluck(:id)
You can also use arel and combine the two queries into one like this:
Dispenser.includes(:reviews, :dispenser_reviews).where((Review.arel_table[:id].not_eq(nil)).or DispenserReview.arel_table[:id].not_eq(nil)).references(:reviews, :dispenser_reviews)

Can you select unique by count in Rails Active Record?

If you can't do it, how would SQL do it?
Basically I want to select all my question objects where there is at least two with the same attribute. That attribute is called, lets say, word_id.
So how would I only select all the objects that that share only once a common attribute with another object?
If I have three objects :
# Question(id: 1, word_id: 1)
# Question(id: 2, word_id: 2)
# Question(id: 3, word_id: 2)
# Question(id: 4, word_id: 1)
# Question(id: 5, word_id: 1)
# Question(id: 6, word_id: 1)
I would want to return just id's 2 and 3 since they both share a common attribute twice.
Is that possible? I crudely do this by making two calls to the DB where first I call all the objects in question, add them to an array, and subtract from that array objects that match my requirements. I was just curious if there was a more elegant way to do it all at once.
Just SQL:
SELECT * FROM questions WHERE world_id IN (
SELECT world_id FROM questions GROUP BY world_id HAVING count(*) = 2
)
Rails:
Question.where("world_id IN (?)", Question.find(:all, select: "world_id",
group: "world_id HAVING count(*) = 2"))
I guess that's still two queries though...

How do I flatten a hierarchy in LINQ to Entities?

I have a class Org, which has ParentId (which points to a Consumer) and Orgs properties, to enable a hierarchy of Org instances. I also have a class Customer, which has a OrgId property. Given any Org instance, named Owner, how can I retrieve all Customer instances for that org? That is, before LINQ I would do a 'manual' traversal of the Org tree with Owner as its root. I'm sure something simpler exists though.
Example: If I have a root level Org called 'Film', with Id '1', and sub-Org called 'Horror' with ParentId of '1', and Id of 23, I want to query for all Customers under Film, so I must get all customers with OrgId's of both 1 and 23.
Linq won't help you with this but SQL Server will.
Create a CTE to generate a flattened list of Org Ids, something like:
CREATE PROCEDURE [dbo].[OrganizationIds]
#rootId int
AS
WITH OrgCte AS
(
SELECT OrganizationId FROM Organizations where OrganizationId = #rootId
UNION ALL
SELECT parent.OrganizationId FROM Organizations parent
INNER JOIN OrgCte child ON parent.Parent_OrganizationId = Child.OrganizationId
)
SELECT * FROM OrgCte
RETURN 0
Now add a function import to your context mapped to this stored procedure. This results in a method on your context (the returned values are nullable int since the original Parent_OrganizationId is declared as INT NULL):
public partial class TestEntities : ObjectContext
{
public ObjectResult<int?> OrganizationIds(int? rootId)
{
...
Now you can use a query like this:
// get all org ids for specific root. This needs to be a separate
// query or LtoE throws an exception regarding nullable int.
var ids = OrganizationIds(2);
// now find all customers
Customers.Where (c => ids.Contains(c.Organization.OrganizationId)).Dump();
Unfortunately, not natively in Entity Framework. You need to build your own solution. Probably you need to iterate up to the root. You can optimize this algorithm by asking EF to get a certain number of parents in one go like this:
...
select new { x.Customer, x.Parent.Customer, x.Parent.Parent.Customer }
You are limited to a statically fixed number of parent with this approach (here: 3), but it will save you 2/3 of the database roundtrips.
Edit: I think I did not get your data model right but I hope the idea is clear.
Edit 2: In response to your comment and edit I have adapted the approach like this:
var rootOrg = ...;
var orgLevels = new [] {
select o from db.Orgs where o == rootOrg, //level 0
select o from db.Orgs where o.ParentOrg == rootOrg, //level 1
select o from db.Orgs where o.ParentOrg.ParentOrg == rootOrg, //level 2
select o from db.Orgs where o.ParentOrg.ParentOrg.ParentOrg == rootOrg, //level 3
};
var setOfAllOrgsInSubtree = orgLevels.Aggregate((a, b) => a.Union(b)); //query for all org levels
var customers = from c in db.Customers where setOfAllOrgsInSubtree.Contains(c.Org) select c;
Notice that this only works for a bounded maximum tree depth. In practice, this is usually the case (like 10 or 20).
Performance will not be great but it is a LINQ-to-Entities-only solution.