Filter condition using "parent" CurrentMember - ssas

Here is the data-set:
CREATE TABLE Movies(id INT, name VARCHAR(50), genre VARCHAR(50), budget DECIMAL(10));
INSERT INTO Movies VALUES
(1, 'Pirates of the Caribbean', 'Fantasy', 379000000),
(2, 'Avengers', 'Superhero', 365000000),
(3, 'Star Wars', 'Science fiction', 275000000),
(4, 'John Carter', 'Science fiction', 264000000),
(5, 'Spider-Man', 'Superhero', 258000000),
(6, 'Harry Potter', 'Fantasy', 250000000),
(7, 'Avatar', 'Science fiction', 237000000);
To filter relatively to a constant value no problem, e.g. to get all the movies with a budget higher than 300M$:
WITH
MEMBER X AS SetToStr(Filter(Movie.[Name].[Name].Members - Movie.[Name].CurrentMember, Measures.Budget > 300000000))
SELECT
Movie.[Name].[Name].Members ON ROWS,
X ON COLUMNS
FROM
Cinema
Which gives:
Avatar {[Movie].[Name].&[Avengers],[Movie].[Name].&[Pirates of the Caribbean]}
Avengers {[Movie].[Name].&[Pirates of the Caribbean]}
Harry Potter {[Movie].[Name].&[Avengers],[Movie].[Name].&[Pirates of the Caribbean]}
John Carter {[Movie].[Name].&[Avengers],[Movie].[Name].&[Pirates of the Caribbean]}
Pirates of the Caribbean {[Movie].[Name].&[Avengers]}
Spider-Man {[Movie].[Name].&[Avengers],[Movie].[Name].&[Pirates of the Caribbean]}
Star Wars {[Movie].[Name].&[Avengers],[Movie].[Name].&[Pirates of the Caribbean]}
But how to compare to the budget of the current movie instead of the hard-coded 300M$ to get the movies more expensive than the current one?
It would give {} for "Pirates of the Caribbean" as it is the most expensive movie.
For "Avengers" it would be { 'Pirates of the Caribbean' } as this is the second most expensive and only "Pirates of the Caribbean" is more expensive.
For "Avatar" it would give all the other movies as it is the less expensive.
The issue is that inside the Filter function's condition CurrentMember refers to the currently tested tuple and not the one currently selected on the ROWS axis.

Instead of using Filter() for each movie, I would first compute an ordered set of movies based on budget values. Then X could be defined using the SubSet and Rank function.
Here is an example using a different schema but I guess you'll get the point easily:
with
set ordered_continents as order( [Geography].[Geography].[Continent], -[Measures].[#Sales] )
member xx as SetToStr( SubSet( ordered_continents, 0, Rank( [Geography].[Geography].currentMember, ordered_continents) - 1))
select {[#Sales], [xx] } on 0, [Geography].[Geography].[Continent] on 1 from [Sales]
I'm not familiar with SSAS so I'm using icCube but I guess the MDX should be very much similar.

Related

SQL Query: finding cheapest car by company

Following YouTube tutorial "Learn PostgreSQL Tutorial - Full Course for Beginners", I replicate teacher's code but yields different result and cannot figure out why.
Table is this simple:
id | make | model | price
-----+------------+--------------+------------
1 | Toyota | Matrix | 25451.36
and so on, 1000 entries.
Querying cheapest model from manufacturer, tutorial says:
SELECT
make, model, MIN(price)
FROM
car
GROUP BY
make, model;
And it works properly, returning as many entries as distinct car makers.
But when I run it in my PostgreSQL terminal returns all 1000 entries disordered.
However, when I query without model's name, I get the right answer, but (obviously)
without the model name as shown below:
make | cheapest
---------------+----------
Fillmore | 72263.48
McLaren | 78510.84
Any suggestions as to why this might happen?
This db-fiddle works as expected. Notice the output. It shows a proper GROUP BY.
Query source:
CREATE TABLE t (
make varchar(40),
model varchar(40),
price integer
);
INSERT INTO t (make, model, price) VALUES
('Fillmore', 'F_M1', 10000),
('Fillmore', 'F_M2', 20000),
('McLaren', 'M_M2', 40000),
('McLaren', 'M_M2', 60000),
('Toyota', 'T_M1', 12000),
('Toyota', 'T_M2', 24000),
('Toyota', 'T_M3', 48000);
SELECT
make, model, MIN(price)
FROM
t
GROUP BY
make, model
ORDER BY make, model;
Result:
Schema (PostgreSQL v10.0)
CREATE TABLE t (
make varchar(40),
model varchar(40),
price integer
);
INSERT INTO t (make, model, price) VALUES
('Fillmore', 'F_M1', 10000),
('Fillmore', 'F_M2', 20000),
('McLaren', 'M_M2', 40000),
('McLaren', 'M_M2', 60000),
('Toyota', 'T_M1', 12000),
('Toyota', 'T_M2', 24000),
('Toyota', 'T_M3', 48000);
Query #1
SELECT
make, model, MIN(price)
FROM
t
GROUP BY
make, model
ORDER BY make, model;
make
model
min
Fillmore
F_M1
10000
Fillmore
F_M2
20000
McLaren
M_M2
40000
Toyota
T_M1
12000
Toyota
T_M2
24000
Toyota
T_M3
48000
View on DB Fiddle

SQL - count function not working correctly

I'm trying to count the blood type for each blood bank I'm using oracle DB
the blood bank table is created like this
CREATE TABLE BloodBank (
BB_ID number(15),
BB_name varchar2(255) not NULL,
B_type varchar2(255),CONSTRAINT
blood_ty_pk FOREIGN KEY
(B_type) references BloodType(B_type),
salary number(15) not Null,
PRIMARY KEY (BB_ID)
);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (370,'new york Blood Bank','A+,A-,B+',12000);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (791,'chicago Blood Bank','B+,AB-,O-',90000);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (246,'los angeles Blood Bank','O+,A-,AB+',4500);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (360,'boston Blood Bank','A+,AB+',13000);
INSERT INTO BloodBank (BB_ID,BB_name,b_type, salary)
VALUES (510,'seattle Blood Bank','AB+,AB-,B+',2300);
select * from BloodBank;
when I use the count function
select count(B_type)
from bloodbank
group by BB_ID;
the result would be like this
so why the count function is not working correctly?
I'm trying to display each blood bank blood type count which is not only one in this case
I hope I don't get downvoted for solving the specific problem you're asking about, but this query would work:
select bb_id,
bb_name,
REGEXP_COUNT(b_type, ',')+1
from bloodbank;
However, this solution ignores a MAJOR issue with your data, which is that you do not normalize it as #Tim Biegeleisen correctly instructs you to do. The solution I've provided is EXTREMELY hacky in that it counts the commas in your string to determine the number of blood types. This is not at all reliable, and you should 100% do what Tim B recommends. But for the circumstances you find yourself in, this will tell you how many different blood types are kept at a specific blood bank.
http://sqlfiddle.com/#!4/8ed1c2/2
You should normalize your data and get each blood type value onto a separate record. That is, your starting data should look like this:
BB_ID | BB_name | b_type | salary
370 | new york Blood Bank | A+ | 12000
370 | new york Blood Bank | A- | 12000
370 | new york Blood Bank | A+ | 12000
... and so on
With this data model, the query you want is something along these lines:
SELECT BB_ID, BB_name, b_type, COUNT(*) AS cnt
FROM bloodbank
GROUP BY BB_ID, BB_name, b_type;
Or, if you want just counts of types across all bloodbanks, then use:
SELECT b_type, COUNT(*) AS cnt
FROM bloodbank
GROUP BY b_type;

How to filter query based on table where they dont share a field?

Ok so i've been working on this SQL statement for while and just cant figure it out. I need to be able to see the top 5 authors that clients have borrowed from in 2017. My tables look like this
1. Client - fields (clientId,clientFirstName,clientlastName,clidentDoB)
2.author - fields (AuthorID, AuthorFirstname, AuthorLastname,AuthorNation )
3. book - fields (BookId, BookAuthor,BookTitle,BookGenre)
4. borrower - fields (borrowID,BorrowDate, ClientID,BookId)
so I understand that I need to pull the names from author table, based on the number of books borrowed, I also understand that borrower.bookId is equal to book.BookId and Author.AuthorID is equal to Book.BookAuthor.
I should be able to set it so that it sees books borrowed in 2017, then filters by the most popular by taking the borrowBookId and adding each instance of the same Id together and seeing what bookID matches BookAuthor in book table and then use that to compare ID to get the first and last name printed.
I have tried
SELECT author.authorfirstname,author.authorlastname
FROM author Join ON author.authorid = book.bookauthor
WHERE (borrower.borrowdate <='31/12/2017' AND borrower.borrowdate >= '01/01/2017');
I know this won't work but Im not sure how to get that bridge from author to borrower.
Sample data and expected output from it.
ok sure. Lets say I have 4 authors and we want the top 3. We also have only borrows in 2017 counting. The client table isn't really needed for this so lets fill in the others with some data. table field names same as in original question sample for this would be
author table
(1,bob,ross, USA)
(2, fred, martin, USA)
(3, alex,joe,CAN)
(4, dan, reed, can)
Book table
(1,1,bobsbook,fantasy)
(2,1,bobagain,fantasy)
(3,1,returnofbob,fantasy)
(4,2,fredsadventure,fantasy)
(5, 2, fedagain, fantasy)
(6, 2, fedstrikes, fantasy)
(7,3,alexjoes, fantasy)
(8, 3, alexjoeagain,fantasy)
(9,4, dansbook, fantasy)
borrow table
(1, 20/01/2017,,1, 1)
(2, 20/01/2017,,3, 2)
(3, 20/01/2017,,2, 1)
(4, 20/01/2017,,1, 3)
(5, 20/01/2017,,6, 2)
(6, 20/01/2017,,8, 4)
(7, 20/01/2017,,4, 4)
(8, 20/01/2017,,9, 6)
(9, 20/01/2017,,2, 7)
(10, 20/01/2017,,3, 9)
(11, 20/01/2017,,4, 9)
the end result would be
AuthorFirstName AuthorLastname
bob ross
Fred Martin
Dan Reed
This is because they had the most borrows in 2017 date range, they are in order with bob at 5, fred at 3 and dan at 2. It also only prints the top 3 people so alex joe is left off the list.
code given to be by #fahmi
SELECT author.authorfirstname,author.authorlastname
FROM author
Join book ON author.authorid = book.bookauthor
join borrower on book.bookid=borrower.bookid
WHERE borrower.borrowdate <='31/12/2017' AND borrower.borrowdate >= '01/01/2017'
;
this has given me a list of the authors and it lists each instance of the burrow but I need the list merged so I only see the name one and so that it is in order by most borrows and limits it self.
Use TOP-N query feature from Oracle,
select au.authorfirstname
,au.authorlastname
from author au
join book bk
on au.authorid = bk.bookauthor
join borrower bw
on bk.bookid=bw.bookid
where extract(year from bw.borrowdate)
= 2017
group by au.authorfirstname
,au.authorlastname
order by count(bk.bookid) desc
fetch first 3 rows with ties;
You need another join to borrower with book.bookid=borrower.bookid relationship
SELECT top 3 author.authorfirstname,author.authorlastname,count(borrower.bookid) as cnt
FROM author
Join book ON author.authorid = book.bookauthor
join borrower on book.bookid=borrower.bookid
WHERE borrower.borrowdate <='31/12/2017' AND borrower.borrowdate >= '01/01/2017'
group by author.authorfirstname,author.authorlastname
order by cnt desc

Ordering a has_many :through by a condition / subquery in rails

I have something like the following (simplified and kittenified):
Kitten has a name
Kitten has many-to-many relationship with VisitedCountries (through KittensVisitedCountry)
VisitedCountry has a country_code such as 'us', 'gb', 'fr' etc.
I want to try and order my kittens by whether they've been to the UK (country code 'gb'), then by age (desc). For example:
bob has been to 'fr' and 'us', and is 3yo
alice has been to 'us' and 'gb' and is 4yo
frances has been nowhere, but is 6yo
colin has been to 'gb' and is 2yo
So in this case, I would want them ordered as:
alice (4) - because she is the oldest that has been to the UK
colin (2) - because he is the youngest that has been to the UK
frances (6) - because she is the oldest that hasn't been to the UK
bob (3)
The closest I've been able to get - and it might work if it wasn't a many-to-many, is the following:
Kitten.joins(:visited_countries)
.select('kittens.*', "(visited_countries.country_code = 'gb') as has_visited_gb")
.order('has_visited_gb DESC', 'age DESC')
That gave me:
alice (4, gb us}
Colin (2, gb}
alice (4, gb us}
bob (3, us fr}
bob (3, us fr}
Obviously this isn't quite what we want - repetition, and poor frances doesn't even appear.
I'm assuming some kind of subquery is needed, but my sql-fu is rusty. I'm using postgres underneath, if that's any help.
Edit: I should mention, I've tried using .includes instead of .joins - I believe it attempts multiple SQL statements when you try that, so the select can't be applied - you get this error:
Kitten Load (0.7ms) SELECT kittens.*, (visited_countries.country_code = 'gb') as has_visited_gb FROM "kittens" ORDER BY has_visited_gb DESC, age DESC LIMIT $1 [["LIMIT", 11]]
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "visited_countries"
LINE 1: SELECT kittens.*, (visited_countries.country_code = 'gb') a...
^
: SELECT kittens.*, (visited_countries.country_code = 'gb') as has_visited_gb FROM "kittens" ORDER BY has_visited_gb DESC, age DESC LIMIT $1
Edit 2:
I've feel like it might simplify it further by ignoring the VisitedCountry table and hard-code the UK's id in the query:
Kitten.joins(:kittens_visited_countries).
select('kittens.*', "(kittens_visited_countries.visited_country_id = 1) as has_visited_gb").
order('has_visited_gb DESC', 'age DESC')
this gives me:
alice (4, gb us}
Colin (2, gb}
alice (4, gb us}
bob (3, us fr}
bob (3, us fr}
But still duplicated, still missing Frances. If I could get .includes() to do a join instead of multiple queries, this would work - but I don't know how to encourage that.

Conditional Sum using VBA

I want a code in VBA, which will get me a sum for a category (which is displayed as a number), note that this number is changing and not in serial order, as soon as it changes, I want the sum for that particular sub-division. Example- say Towns is a category and Ward is sub category, so my data is displayed as following:
So lets say the data says that town numbered 100 has 3 wards (1001, 1002, 1003) with their population data,and town numbered 123 has 2 wards (1231, 1232) with their total population data correspondingly, now I want to sum the total population in town 100 and similarly for town 123..and so on.
It sounds like a Pivot Table will do what you want and you won't need any custom VBA code at all.