Related
After trying a lot of approaches and formulas I decided to ask this question.
See this Matrix visual:
WeekDate is column in a table called Planning. It's related to another date time column Week_Imported in another table called Export.
Export table only has values for the dates: 23-Dec-19, 30-Dec-19 and 06-Jan-20 whereas Planning table has dates spanning multiple weeks in the past and future.
Cumulative Plan Count is calculating correctly as long as there are matching dates between the tables Planning and Export.
Now I'd like to keep calculating even when there's no date matching. I want to get the value 32 from the FIRSTDATE which has data (in this case 23-Dec-2019) and backfill the past with 32.
For dates in the future I'd like to use LASTDATE (06-Jan-20) value which is 89.
Something like this:
WeekDate Cumulative Plan Count
.
.
.
25-Nov-19 32
02-Dec-19 32
09-Dec-19 32
16-Dec-19 32
23-Dec-19 32 <= First WeekDate which has data [backfill past with 32]
30-Dec-19 57
06-Jan-19 89 <= Last WeekDate which has data [fill future with 89]
13-Jan-20 89
20-Jan-20 89
27-Jan-20 89
.
.
.
The formula used for the cumulative SUM is this:
Cumulative Plan Count =
CALCULATE (
ROUNDUP([1 - Target] * MAX(Planning[1 - Plan]), 0)
,
FILTER (
ALL ( Planning[WeekDate] ),
Planning[WeekDate] <= MAX(Planning[WeekDate])
)
)
####### Edit 1 #######
Using this measure below I get 1s for the past...
1 - Target =
VAR minWeek = MIN(Export[Week_Imported])
VAR targetCount =
CALCULATE (
COUNT( 'Export'[1 - Plan]),
FILTER('Export', OR(Export[1 - Plan]="YES", Export[1 - Plan]="_")))
var minTarget = CALCULATE (
COUNT( 'Export'[1 - Plan]),
FILTER('Export', OR(Export[1 - Plan]="YES", Export[1 - Plan]="_")
&& Export[Week_Imported] = minWeek))
RETURN
SWITCH(TRUE,
targetCount = BLANK(), 1, // Here is where I need to get the 1st row value (32) and fill up the column up above...
targetCount)
The problem is that no matter what I do I can't get the 1st value for 23-Dec-2019 (32) to fill up the Cumulative Plan Count column.
This is the result when I use the formula above:
WeekDate Cumulative Plan Count
.
.
.
25-Nov-19 1
02-Dec-19 1
09-Dec-19 1
16-Dec-19 1
23-Dec-19 32 <= First WeekDate which has data
30-Dec-19 57
06-Jan-19 89 <= Last WeekDate which has data
13-Jan-20 89
20-Jan-20 89
27-Jan-20 89
.
.
.
####### Edit 2 #######
I put together a simplified Sample.pbix which shows what I'm trying to
accomplish with minimum data to test things:
https://drive.google.com/drive/folders/1zxS_2VE9_0JEMXvsg9Dq196BK552RbNo?usp=sharing
This screenshot has more details:
https://drive.google.com/open?id=1_-IMEpLwuWWN6vrrT_TNWbeqZ7f1LOan
Let me introduce the solution with intermediate steps.
In your data schema, Planning and Export tables are in one to many relationship. Planning is in the grain of every week, while Export has more rows for each week.
Relationship diagram
On this basis, the measure to count the number of Export rows for each Planning week is as simple as this.
Plan Count (Basic) = COUNTROWS ( 'Export' )
When you slice by Planning[WeekDate], this measure returns the counts of Export rows for the corresponding weeks.
Actually, you need FILTER to count only rows you are interested in.
Plan Count =
COUNTROWS (
FILTER (
'Export',
OR ( 'Export'[Plan] = "YES", 'Export'[Plan] = "_" )
)
)
Here is the result we get so far.
Having this measure as the starting point, we need to extend the calculation to the periods where the data does not exist. In order to do that, we need to handle the filter context where [Plan Count] is evaluated.
We need to get the first and final weeks where Export data exists. Here is a formula that returns the first date of the data regardless of the slicer.
First Data Week =
CALCULATE (
MIN ( Planning[WeekDate] ),
REMOVEFILTERS ( Planning[WeekDate] ),
TREATAS (
CALCULATETABLE (
VALUES ( 'Export'[Week_Imported] ), -- Foreign key referencing Planning[WeekDate]
REMOVEFILTERS ( Planning )
),
Planning[WeekDate]
)
)
We can use this date to modify the filter context to calculate [Plan Count] of the first data week. In fact, below measure always returns 1064, which is the number of [Plan Count] in December 30, 2019.
First Week Plan Count =
VAR _FirstDataWeek = CALCULATE (
MIN ( Planning[WeekDate] ),
REMOVEFILTERS ( Planning[WeekDate] ),
TREATAS (
CALCULATETABLE (
VALUES ( 'Export'[Week_Imported] ),
REMOVEFILTERS ( Planning )
),
Planning[WeekDate]
)
)
RETURN
CALCULATE (
[Plan Count],
Planning[WeekDate] = _FirstDataWeek
)
Using this technique, we can expand the first and final values of [Plan Count] to the past and the future dates. Below is the final formula, which iterates over Planning table, and apply different filter context to calculate [Plan Count].
Extended Plan Count =
-- Planning[WeekDate] values where Export data exists
VAR _DataWeeks = CALCULATETABLE (
VALUES ( Planning[WeekDate] ),
REMOVEFILTERS ( Planning[WeekDate] ),
TREATAS (
CALCULATETABLE (
VALUES ( 'Export'[Week_Imported] ), -- Foreign key referencing Planning[WeekDate]
REMOVEFILTERS ( Planning )
),
Planning[WeekDate]
)
)
-- First and last Planning[WeekDate] where Export data exists
VAR _FirstDataWeek = MINX ( _DataWeeks, [WeekDate] )
VAR _FinalDataWeek = MAXX ( _DataWeeks, [WeekDate] )
-- [Plan Count] values of first and last weeks
VAR _FirstDataWeekPlanCount = CALCULATE ( [Plan Count], Planning[WeekDate] = _FirstDataWeek )
VAR _FinalDataWeekPlanCount = CALCULATE ( [Plan Count], Planning[WeekDate] = _FinalDataWeek )
RETURN
SUMX (
Planning,
SWITCH (
TRUE,
Planning[WeekDate] < _FirstDataWeek, _FirstDataWeekPlanCount,
Planning[WeekDate] > _FinalDataWeek, _FinalDataWeekPlanCount,
[Plan Count]
)
)
I am a newbie here.
I am using PostgreSQL to manipulate lots of data in my specific field of research. Unfortunately, I am encountering a problem that is not allowing me to continue my analysis. I tried to simplify my problem to clearly illustrate it.
Let's suppose I have a table called "Buyers" with those data:
table_buyers
The buyers can make ONLY ONE purchase in each store or none. There are three stores and there a table for each one. Just like below:
table_store1
table_store2
table_store3
To create the tables, I am using the following code:
CREATE TABLE public.buyer
(
ID integer NOT NULL PRIMARY KEY,
name text NOT NULL,
phone text NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store1
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store2
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
CREATE TABLE public.Store3
(
ID_buyer integer NOT NULL PRIMARY KEY,
total_order numeric NOT NULL,
total_itens integer NOT NULL
)
WITH (
OIDS = FALSE
)
;
To add the information on the tables, I am using the following code:
INSERT INTO buyer (ID, name, phone) VALUES
(1, 'Alex', 88888888),
(2, 'Igor', 77777777),
(3, 'Mike', 66666666);
INSERT INTO Store1 (ID_buyer, total_order, total_itens) VALUES
(1, 87.45, 8),
(2, 14.00, 3),
(3, 12.40, 4);
INSERT INTO Store2 (ID_buyer, total_order, total_itens) VALUES
(1, 785.12, 7),
(2, 9874.21, 25);
INSERT INTO Store3 (ID_buyer, total_order, total_itens) VALUES
(2, 45.87, 1);
As all the tables are interconnected by buyer's ID, I wish I could have a query that generates an output just like this:
desired output table.
Please, note that if the buyer did not buy anything in a store, I must print '0'.
I know this is an easy task, but unfortunately, I have been failing on accomplish it.
Using the 'AND' logical operator, I tried the following code to accomplish this task:
SELECT
buyer.id,
buyer.name,
store1.total_order,
store2.total_order,
store3.total_order
FROM
public.buyer,
public.store1,
public.store2,
public.store3
WHERE
buyer.id = store1.id_buyer AND
buyer.id = store2.id_buyer AND
buyer.id = store3.id_buyer;
But, obviously, it just returned 'Igor' as this was the only buyer that have bought items on all three stores (print screen).
Then, I tried the 'OR' logical operator, just like the following code:
SELECT
buyer.id,
buyer.name,
store1.total_order,
store2.total_order,
store3.total_order
FROM
public.buyer,
public.store1,
public.store2,
public.store3
WHERE
buyer.id = store1.id_buyer OR
buyer.id = store2.id_buyer OR
buyer.id = store3.id_buyer;
But then, it returns 12 lines with wrong values (print screen).
Clearly, my mistake is about not considering that 'Buyers' don't have to on all three stores on my code. I just can't correct it on my own, can you please help me?
I appreciate a lot for an answer that can light up my way. Thanks a lot!
Tips about how I can search for this issue are very welcome as well!
Ok. I doubt that this is the final answer for you, but its a start
SELECT
buyer.id,
buyer.name,
COALESCE( gb_store1.total_orders, 0 ) as store1_total,
COALESCE( gb_store2.total_orders, 0 ) as store2_total,
COALESCE( gb_store3.total_orders, 0 ) as store3_total
FROM
public.buyer,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store1
GROUP BY ID_buyer ) gb_store1 ON gb_store1.id_buyer = buyer.id ,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store2
GROUP BY ID_buyer ) gb_store2 ON gb_store2.id_buyer = buyer.id ,
LEFT OUTER JOIN ( SELECT ID_buyer,
SUM( total_orders ) as total_orders,
SUM( total_itens ) as total_itens
FROM public.store3
GROUP BY ID_buyer ) gb_store3 ON gb_store3.id_buyer = buyer.id ;
So, this query has a couple elements should focus on. The subselects/groupby allow you to total within your subtables by ID_buyer. The LEFT OUTER JOIN make its so your query can still return a result, even if a subselect finds no matching record. Finally, the COALESCE allows you to return 0 when one of your totals is NULL (because the subselect found no match).
Hope this helps.
I am totally new in MDX. I'd need to filter all records containing substring "SBP1". It works for me when I enter each record separately like this:
{[Article].[Article Alternative ID CPG].[SBP1 0],[Article].[Article Alternative ID CPG].[SBP1 1],[Article].[Article Alternative ID CPG].[SBP1 W]}
MDX builder screenshot
Working code:
SELECT
NON EMPTY
{[Measures].[Value]} ON COLUMNS
,NON EMPTY
{
[Article].[Article Alternative ID CPG].[Article Alternative ID CPG].ALLMEMBERS*
[Article].[Market].[Market].ALLMEMBERS*
[Article].[Brand].[Brand].ALLMEMBERS*
[Article].[Product].[Product].ALLMEMBERS
}
DIMENSION PROPERTIES
MEMBER_CAPTION
,MEMBER_UNIQUE_NAME
ON ROWS
FROM
(
SELECT
{
[Article].[Article Alternative ID CPG].[SBP1 0]
,[Article].[Article Alternative ID CPG].[SBP1 1]
,[Article].[Article Alternative ID CPG].[SBP1 W]
} ON COLUMNS
FROM
(
SELECT
{[Measure Data Type].[Data Type].[Actuals]} ON COLUMNS
FROM
(
SELECT
{[Org Sales Area].[Sales Organization Key].[DEB1]} ON COLUMNS
FROM
(
SELECT
{[Org Sales Area].[Distribution Channel].&[DO]} ON COLUMNS
FROM
(
SELECT
{[Org Business Unit].[Business Unit].[U-K]} ON COLUMNS
FROM
(
SELECT
{[Measure Item].[Measure Item].&[KF00310]} ON COLUMNS
FROM
(
SELECT
{[Date of Posting].[Posting Y Q M D].[Posting Year].&[2017]} ON COLUMNS
FROM [COLOR LEAN CPG]
)
)
)
)
)
)
)
WHERE
(
[Date of Posting].[Posting Y Q M D].[Posting Year].&[2017]
,[Measure Item].[Measure Item].&[KF00310]
,[Org Business Unit].[Business Unit].[U-K]
,[Org Sales Area].[Distribution Channel].&[DO]
,[Org Sales Area].[Sales Organization Key].[DEB1]
,[Measure Data Type].[Data Type].[Actuals]
)
CELL PROPERTIES
VALUE
,BACK_COLOR
,FORE_COLOR
,FORMATTED_VALUE
,FORMAT_STRING
,FONT_NAME
,FONT_SIZE
,FONT_FLAG;
How could I do it more efficient with instr function?
I tried this but doesn't work:
SELECT
[Measures].[Value] ON COLUMNS
,Filter
(
[Article].[Article Alternative ID CPG].[Article Alternative ID CPG].ALLMEMBERS
,
Instr
(
[Article].[Article Alternative ID CPG].[Article Alternative ID CPG].CurrentMember.Name
,'SBP1'
)
> 0
) ON ROWS
FROM [COLOR LEAN CPG];
What is wrong on my MDX statament?
MDX with my SELECT screenshot
Thank you
I found a valid MDX statement:
Filter( [Article].[Main Article Alternative ID CPG].[Main Article Alternative ID CPG].ALLMEMBERS,Instr( [Article].[Main Article Alternative ID CPG].currentmember.Properties( 'Member_Caption' ), 'SBP1' ) > 0)
But this solution I didn't know to transform to pure GUI MDX BUILDER a insert it there.
I think you might have navigated a little far when applying the CURRENTMEMBER function. Try this:
SELECT
[Measures].[Value] ON COLUMNS
,Filter
(
[Article].[Article Alternative ID CPG].[Article Alternative ID CPG].ALLMEMBERS
,
Instr
(
[Article].[Article Alternative ID CPG].CurrentMember.Name //<<[Article].[Article Alternative ID CPG].[Article Alternative ID CPG].CurrentMember.Name
,'SBP1'
)
> 0
) ON ROWS
FROM [COLOR LEAN CPG];
Hopefully name works for you but I usually go for MEMBERCAPTION:
SELECT
[Measures].[Value] ON COLUMNS
,Filter
(
[Article].[Article Alternative ID CPG].[Article Alternative ID CPG].ALLMEMBERS
,
Instr
(
[Article].[Article Alternative ID CPG].CurrentMember.MEMBERCAPTION
,'SBP1'
)
> 0
) ON ROWS
FROM [COLOR LEAN CPG];
As an extension to this question, is it possible to incorporate wildcards with postgres?
E.g. Something like this (note the wildcard to select all entries where key_part_1 has a value of 'C'):
SELECT *
FROM table_name
WHERE (key_part_1, key_part_2) IN ( ('B',1), ('C',?) );
Is this possible, and if so, what's the syntax?
If key_part_2 can be anything, there is no need to test it.
SELECT *
FROM table_name
WHERE (key_part_1, key_part_2) IN ( ('B',1) )
OR key_part_1 IN('C' )
;
You can use the same column in IN clause(if both columns are not nullable):
SELECT *
FROM table_name
WHERE (key_part_1, key_part_2) IN ( ('B',1), ('C',key_part_2) );
So for second value pair only key_part_1='C' will be used because key_part_2=key_part_2 is always true.
WHERE (key_part_1, key_part_2) IN ( ('B',1), ('C',key_part_2) );
<=>
WHERE (key_part_1='B' AND key_part_2 = 1)
OR (key_part_1='C' AND key_part_2 = key_part_2);
If columns can be nullable you can wrap it with COALESCE(column_name, value) like:
SELECT *
FROM tab
WHERE (key_part_1, COALESCE(key_part_2,-1)) IN ( ('B',1),
('C',COALESCE(key_part_2,-1)) );
SqlFiddleDemo
Note that value used in COALESCE should be distinct for actual data to avoid colisions.
I wish to write a Query for SAP B1 (t-sql) that will list all Income and Expenses Items by total and month by month.
I have successfully written a Query using PIVOT, but I do not want the column headings to be hardcoded like: Jan-11, Feb-11, Mar-11 ... Dec-11.
Rather I want the column headings to be parametrically generated, so that if I input:
--------------------------------------
Query - Selection Criteria
--------------------------------------
Posting Date greater or equal 01.09.10
Posting Date smaller or equal 31.08.11
[OK] [Cancel]
the Query will generate the following columns:
Sep-10, Oct-10, Nov-10, ..... Aug-11
I guess DYNAMIC PIVOT can do the trick.
So, I modified one SQL obtained from another forum to suit my purpose, but it does not work. The error message I get is Incorrect Syntax near 20100901.
Could anybody help me locate my error?
Note: In SAP B1, '[%1]' is an input variable
Here's my query:
/*Section 1*/
DECLARE #listCol VARCHAR(2000)
DECLARE #query VARCHAR(4000)
-------------------------------------
/*Section 2*/
SELECT #listCol =
STUFF(
( SELECT DISTINCT '],[' + CONVERT(VARCHAR, MONTH(T0.RefDate), 102)
FROM JDT1
FOR XML PATH(''))
, 1, 2, '') + ']'
------------------------------------
/*Section 3*/
SET #query = '
SELECT * FROM
(
SELECT
T0.Account,
T1.GroupMask,
T1.AcctName,
MONTH(T0.RefDate) as [Month],
(T0.Debit - T0.Credit) as [Amount]
FROM dbo.JDT1 T0
JOIN dbo.OACT T1 ON T0.Account = T1.AcctCode
WHERE
T1.GroupMask IN (4,5,6,7) AND
T0.[Refdate] >= '[%1]' AND
T0.[Refdate] <= '[%2]'
) S
PIVOT
(
Sum(Amount)
FOR [Month] IN ('+#listCol+')
) AS pvt
'
--------------------------------------------
/*Section 4*/
EXECUTE (#query)
I don't know SAP, but a couple of things spring to mind:
It looks like you want #listCol to contain a collection of numbers within square brackets, for example [07],[08],[09].... However, your code appears not to put a [ at the start of this string.
Try replacing the lines
T0.[Refdate] >= '[%1]' AND
T0.[Refdate] <= '[%2]'
with
T0.[Refdate] >= ''[%1]'' AND
T0.[Refdate] <= ''[%2]''
(I also added a space before the AND in the first of these two lines while I was editing your question.)