Tableau: A bar which shows the difference between - tableau-api

I have the following data for which i need to create a bar chart as shown in the picture in tableau:
Brand Sales week
A 12 1
B 20 1
C 14 1
A 12 2
B 22 2
C 16 2
A 18 3
B 16 3
C 27 3
My chart must contain sales in rows and week in columns. For every week, I must show three bars:
Sales for Brand A (A is always fixed)
sales for Brand B/ Brand C (B/C:- Its a parameter selection)
Difference between sales of these two

Is this what you asking for ??

You can do this by creating calculated fields of the sales for Brand A, Brand B/C (based on selection), and the difference between those two calculated fields:
Brand A Sales: SUM(IF [Brand] = 'A' THEN [Sales] END)
Brand B/C Sales: SUM(IF [Brand] != 'A' THEN [Sales] END)
Brand Difference: [Brand A Sales] - [Brand B/C Sales]
The calculation is dependent on your filter being setup properly, so make sure you have something like this calculated field in your filter card and set to True:
Brand Filter: [Brand Select] = [Brand] OR [Brand] = 'A' [Brand Select] is the parameter to select between B or C
Lastly, place [Measure Names] in the columns section and [Measure Values] in the rows section. Be sure only to include the 3 calculated fields mentioned above.
You should get a result like so:
Brand B -
Brand C -
Note: The calculation for the difference between brands may need to be altered based on your expected output. I based mine in the example off of what I assumed you had within the image in your post.

Related

Distinct Count after Sum

So I am looking to do a count after aggregation. Basically I want to be able to total up the Inventory count with a sum and then count how many times each employee has a non zero inventory count.
So for this data Jack/Jimmy would have a count of 1, Sam would have a count of 2 and Steve would have a count of 0. I could easily do this in SQL on the back end but I also want them to be able to use a date parameter. So if they shifted the date to only 1/1/17 Sam would have a count of 1 and everyone else would have a 0. Any help would be much appreciated!
Data
Emp Item Inventory Date
Sam Crackers 1 1/1/2017
Jack Crackers 1 1/1/2017
Jack Crackers -1 2/1/2017
Jimmy Crackers -2 1/1/2017
Sam Apples 1 1/1/2017
Steve Apples -1 1/1/2017
Sam Cheese 1 1/1/2017
With Date>= '1/1/17':
Emp NonZeroCount
Sam 2
Jack 1
Jimmy 1
Steve 0
With Date = '1/1/17':
Emp NonZeroCount
Sam 1
Jack 0
Jimmy 0
Steve 0
SQL I envision it replacing
Create Table #Test(
Empl varchar(50),
Item Varchar (50),
Inventory int,
Date Date
)
Declare #DateParam Date
Set #DateParam = '1/1/17'
Insert into #Test (Empl,Item,Inventory,Date)
Values
('Sam','Crackers',1,'1/1/2017'),
('Jack','Crackers',1,'1/1/2017'),
('Jack','Crackers',-1,'2/1/2017'),
('Jimmy','Crackers',-2,'1/1/2017'),
('Sam','Apples',1,'1/1/2017'),
('Steve','Apples',-1,'1/1/2017'),
('Sam','Cheese',1,'1/1/2017');
Select
Item,Sum(Inventory) as Total
into #badItems
from #Test
Where Date >= #DateParam
group by Item
having Sum(Inventory) <> 0
Select
T.Empl,Count(Distinct BI.Item)
From #Test T
Inner Join #badItems BI on BI.Item = T.Item
group by T.Empl
This is a good case for creating a set in Tableau.
Select the Item field in the data pane on the left, and right click to create a set based on that field. Name it Bad Items, and define it using the following formula on the Condition tab, which assumes you've defined a parameter named [DateParam] of type Date.
sum(if [Date] >= [DateParam] then [Inventory] end) <> 0
You can then use the set on the filter shelf, row shelf, in calculations or combine with other sets as desired.
P.S. I used an alias to display the text "Bad Items" instead of "In" in the table, set a manual default sort order for the Emp field (in case you are trying to reproduce this exactly)

How to achieve matching with same table in postgresql?

I want to count number of sites partition by country on the basis of below criteria:-
For each TrialSite with Trial_Start_Date = X and Site_Activated = Y, you should be counting all rows that meet these conditions:
Trial_Start_Date <= Y, AND
TrialSite_Activation_Date >= X
i.e. all rows where there is some overlapping period with that row's Trial Start Date to TrialSite Activation Date.
sample data example:
Trial id start_date country site_id trialSite_activation_date
Trial A 01-01-2017 India site1 01-02-2017 ----> 2 (only overlaps with itself, and with 2nd row)
Trial A 01-01-2017 India site 2 01-04-2017 ----> 4 (overlaps with all rows, including itself)
Trial B 02-03-2017 India site3 01-04-2017 ----> 3 (does not overlap with first row, since Trial_Start_Date > 01-02-2017)
Trial B 02-03-2017 India site4 01-04-2017 ----> 3
This data can contain multiple countries and this logic needs to be applied with same country records.
You could use the “overlaps” operator && on dateranges:
SELECT t1, count(*)
FROM trial t1
JOIN trial t2
ON daterange(t1.trial_start_date, t1.trialsite_activation_date, '[]')
&& daterange(t2.trial_start_date, t2.trialsite_activation_date, '[]')
GROUP BY t1;
t1 | count
-----------------------------------------------+-------
("Trial A",2017-01-01,India,site1,2017-02-01) | 2
("Trial A",2017-01-01,India,site2,2017-04-01) | 4
("Trial B",2017-03-02,India,site3,2017-04-01) | 3
("Trial B",2017-03-02,India,site4,2017-04-01) | 3
(4 rows)
Instead of using the whole-row reference t1 in the SELECT list, you can specify individual columns there, but then you gave to list them in the GROUP BY clause as well.

How to calculate market share in tableau

I have a data set for 10 countries. Each country has more than 8 products and my company has 3 products A,B,C which are sold in each of the country.
So Now, I want to calculate the market share for my products country wise.
for E.g. If total sale for 8 products in country 1 is 100 and sale for products A+B+C is 35, then market share of my company in country1 is 35/100= 35%
.
Please help !
It depends a bit on what you want to do with that afterwards, if you are just interested in the number, do the following:
Create a calculated field TotalSalesPerCountry with {fixed [country]: sum([Sales])} (calculates the total sales per country)
Create a calculated field CompanySalesPerCountry with {fixed [country]: sum(IIF([Product] = 'A' OR [Product] = 'B' OR [Product] = 'C',[Sales],0))} (calculates the total sum per country where [PRODUCT] = A, B or C)
Create a calculated field MarketShare with AVG([CompanySalesPerCountry]) / AVG([TotalSalesPerCountry])
Change the Properties for the last field to 'Percent', drag [Country] to rows and [MarketShare] to columns

combining results of CTEs

I have several CTEs. CTE1A counts number of type A shops in area 1. CTE1B counts number of type B shops in area 1 and so on up to CTE1D. Similarly, CTE2B counts number of type B shops in area 2 and so on. shop_types CTE selects all types of shops: A,B,C,D. How to display a table that shows for each area (column) how many shops of each type there is (rows).
For example:
1 2 3 4 5
A 0 7 4 0 0
B 2 3 8 2 9
C 8 5 8 1 6
D 7 1 5 4 3
Database has 2 tables:
Table regions: shop_id, region_id
Table shops: shop_id, shop_type
WITH
shop_types AS (SELECT DISTINCT shops.shop_type AS type FROM shops WHERE shops.shop_type!='-9999' AND shops.shop_type!='Other'),
cte1A AS (
SELECT regions.region_id, COUNT(regions.shop_id) AS shops_number, shops.shop_type
FROM regions
RIGHT JOIN shops
ON shops.shop_id=regions.shop_id
WHERE regions.region_id=1
AND shops.shop_type='A'
GROUP BY shops.shop_type,regions.region_id)
SELECT * FROM cte1A
I'm not entirely sure I understand why you are after, but it seems you are looking for something like this:
select sh.shop_type,
count(case when r.region_id = 1 then 1 end) as region_1_count,
count(case when r.region_id = 2 then 1 end) as region_2_count,
count(case when r.region_id = 3 then 1 end) as region_3_count
from shops sh
left join regions r on r.shop_id = sh.shop_id
group by sh.shop_type
order by sh.shop_type;
You need to add one case statement for each region you want to have in the output.
If you are using Postgres 9.4 you can replace the case statements using a filter condition which kind of makes the intention a bit easier to understand (I think)
count(*) filter (where r.region_id = 1) as region_1_count,
count(*) filter (where r.region_id = 2) as region_2_count,
...
SQLFiddle: http://sqlfiddle.com/#!1/98391/1
And before you ask: no you can't make the number of columns "dynamic" based on a select statement. The column list for a query must be defined before the statement is actually executed.

TSQL Calculating daily sum across records with overlapping date ranges

I have 3 tables, 1 (PortfolioInstrument) holds instruments (Instrument) held in a portoflio with the holding (Holding) across a date range (DateAdded, DateRemoved).
Another (Price) holds daily (TradeDate) closing prices ([Close]) for each instrument ( Instrument).
A 3rd may be useful, (CalcDate) holds the dates (CalcDate) that we re-calculate the holdings and add and delete instruments from the portfolio.
SELECT SUM([Close]*Holding), TradeDate
FROM Price p1 INNER JOIN PortfolioInstrument pio ON pio.Instrument = p1.Instrument
AND pio.Portfolio = 3
WHERE EXISTS (SELECT TradeDate FROM Price p
INNER JOIN PortfolioInstrument pi ON pi.Instrument = p.Instrument AND Portfolio = 3
WHERE TradeDate >= pi.DateAdded AND
(TradeDate < pi.DateRemoved OR pi.DateRemoved IS NULL)
AND p1.ID = p.ID GROUP BY TradeDate) GROUP BY TradeDate
Here is a sample of the PortfolioInstrument data set
ID Portfolio Instrument Holding DateAdded DateRemoved
16256 3 410 714.28571 2007-10-01 00:00:00.0 2007-11-01 00:00:00.0
16257 3 611 564.97174 2007-10-01 00:00:00.0 2007-11-01 00:00:00.0
16258 3 538 1,797.75281 2007-10-01 00:00:00.0 2007-11-01 00:00:00.0
...
16302 3 5352 1,067,319.75 2008-02-01 00:00:00.0 2008-04-01 00:00:00.0
16303 3 5353 1,057,800.875 2008-02-01 00:00:00.0 2008-04-01 00:00:00.0
16304 3 11952 0 2008-02-29 00:00:00.0 2008-04-01 00:00:00.0
16305 3 11952 261,484,400 2008-04-01 00:00:00.0 2008-05-01 00:00:00.0
...
16315 3 8374 14,199.99902 2009-01-30 00:00:00.0 <null>
16316 3 11952 246,102,960 2009-01-30 00:00:00.0 2009-02-27 00:00:00.0
16317 3 11952 246,148,912 2009-02-27 00:00:00.0 2009-04-01 00:00:00.0
The problem with this is that it includes all Holdings that have a DateRemoved < TradeDate so there is a jump each re-calculation date where they should get removed from the set. Had a look at various DateDiff methods on Stackoverflow but cannot work out how to group using them in this case. Also note that the cash instrument (Instrument = 11952) comes into the portfolio at some point and then gets an entry for every month thereafter, as you can see it reduces to 0 for some months, this should not matter I think in the SQL produced.
Thx.
David
It is not very clear why you are using another instance of the same join like that. If you want to exclude particular holdings where DateRemoved <= TradeDate, you could just check that directly in the WHERE clause:
SELECT SUM(p1.[Close]*pio.Holding), TradeDate
FROM Price p1
INNER JOIN PortfolioInstrument pio
ON pio.Instrument = p1.Instrument AND pio.Portfolio = 3
WHERE p1.TradeDate >= pio.DateAdded
AND (p1.TradeDate < pio.DateRemoved OR pio.DateRemoved IS NULL)
GROUP BY p1.TradeDate
;
However, if you want to discard an entire group of same TradeDate rows in which at least one row satisfies the condition DateRemoved <= TradeDate, you could use a HAVING clause, like this:
SELECT SUM(p1.[Close]*pio.Holding), TradeDate
FROM Price p1
INNER JOIN PortfolioInstrument pio
ON pio.Instrument = p1.Instrument AND pio.Portfolio = 3
GROUP BY p1.TradeDate
HAVING COUNT(CASE WHEN p1.TradeDate <= pio.DateRemoved) THEN 1 END) = 0
;
Unlike a WHERE clause, which applies to individual rows, HAVING is evaluated for a group of rows. In this case, the COUNT() function is used to count how many rows in the group have p1.TradeDate <= pio.DateRemoved. If there's at least one, the group will be discarded from the output, because the requirement that I'm assuming here is that there be no such rows.
Could not find a way forward with this, ended up pulling in the raw rows and doing the calculations in the code.