How to have a child group span across three columns? - sql-server

What I'm trying to do
In my report, I am trying to get some basic data in a tablix. In this tablix there is one main summary row and detail rows inside it. What I want to do is put the details in the child row but split into three columns.
For example my tablix looks like this right now
Row11| Row12| Row13 |
1 | 5 | 4 |
| Column1 | Column2|
| 1 | 4 |
| 2 | 5 |
| 3 | 6 |
2 | 20 | 25 |
Column1 Column2 |
| 7 | 8 |
| 9 | 5 |
| 3 | 6 |
(This is just a demo table. The number of columns in my application is not necessarily this number and it should be irrelevant anyway)
How I want it to look like:
Row11| Row12| Row13 |
1 | 5 | 4 |
| Column1 | Column2| Column1 | Column2| Column1 | Column2|
| 1 | 4 | 2 | 5 | 3 | 6 |
2 | 20 | 25 |
| Column1 | Column2| Column1 | Column2| Column1 | Column2|
| 7 | 8 | 9 | 5 | 3 | 6 |
I just want to split the detail table into three columns. I have tried various approaches but in vein.
What approaches have I tried?
Attaching a sub report method. I attached a sub report and divided the report into three separate tables and split the columns in this order. This works except that it is terribly slow when trying to get large amount of data. Really do not want to do this.
The method mentioned here. Did not work.
I have been experimenting with the SQL itself as well but SQL does not look like to be an issue here.
Tried with Matrix instead of tablix too trying to push my limits but did not succeed.
Side note: If it matters I am using SSRS SDK for PHP and grabbing the PDFs from the Report Server and using Visual Studio to design the reports.
This seems such a simple thing but I am stuck with this. Has anybody in a situation like this before?
Please let me know if you need more clarifications.

Create three detail tables, adjust which rows get shown in each, and put them in a List.
This solution works on the assumption that your raw data looks something like this:
Add a table report item and add the Column1 and Column2 data to it, leaving the grouping as just the details. Right-click the detail row, and go to Row Visibility.
Switch this to 'Show or hide based on an expression', and add this expression:
=IIF(RowNumber("tblFirstColumn") MOD 3 = 1, False, True)
This will make only the first, fourth, seventh etc. record show in that table. Paste two copies of this table next to the first, and adjust the row visibility expression on each:
=IIF(RowNumber("tblSecondColumn") MOD 3 = 2, False, True)
=IIF(RowNumber("tblThirdColumn") MOD 3 = 0, False, True)
Next add a List item. Change the row grouping of the list to group by Row11, add each row field to the top of this list (as text boxes or a non-grouped table), and move the three detail tables into the bottom of the list.
This should perform better than using subreports. I understand that when using subreports the datasets will be queried with every instance of that subreport. With all the design in one report, the queries should only run once.

Method 1: main tablix = three columns with TWO detail rows. In the 2nd detail row merge the three columns together. Create a new tablix for the detail information and put it inside the merged detail cell.
Method 2: main tablix = six columns and two detail rows. In the 1st detail row merge cells 1/2, 3/4, and 5/6 together.

I had to tackle a same problem once and the way I did it is by "Inserting a TABLIX inside a TABLIX". I believe if you follow the link below that shuold resolve what you are looking for:
http://www.sqlcircuit.com/2012/03/ssrs-how-to-show-tablix-inside-tablix.html
Waht I have additionaly done in my report to increase the width of the nested tablix so that it does not affect the width on the main tablix is:
1) On the the row above the insertted tablix I have created a column and kept it empty and merged the cell below it where the nested tablix should be.
2) now you can increase the size of the empty column (make the borders invisible) to what ever the width of the inserted TABLIX you would like.
Hope this helped.

For what it's worth (I see you have already accepted an answer), I think this could be done primarily within SQL if you wished.
Assuming your raw data looks like this:
/-------------------------------------------\
| Row11 | Row12 | Row13 | Column1 | Column2 |
|-------+-------+-------+---------+---------|
| 1 | 5 | 4 | 1 | 4 |
| 1 | 5 | 4 | 2 | 5 |
| 1 | 5 | 4 | 3 | 6 |
| 2 | 20 | 25 | 3 | 6 |
| 2 | 20 | 25 | 7 | 8 |
| 2 | 20 | 25 | 9 | 5 |
\-------------------------------------------/
Let's create demo data to illustrate:
CREATE TABLE data (
Row11 INT,
Row12 INT,
Row13 INT,
Column1 INT,
Column2 INT
)
INSERT INTO data
SELECT 1,5,4,1,4
UNION
SELECT 1,5,4,2,5
UNION
SELECT 1,5,4,3,6
UNION
SELECT 2,20,25,7,8
UNION
SELECT 2,20,25,9,5
UNION
SELECT 2,20,25,3,6
You could aggregate each summary and detail row like this:
SELECT DISTINCT d.Row11,
d.Row12,
d.Row13,
dfirst.Column1,
dfirst.Column2,
dsecond.Column1,
dsecond.Column2,
dthird.Column1,
dthird.Column2
FROM data d
CROSS APPLY
(
SELECT TOP 1 Column1, Column2
FROM data d1
WHERE d1.Row11 = d.Row11 AND d1.Row12 = d.Row12 AND d1.Row13 = d.Row13
ORDER BY 1,2
) dfirst
CROSS APPLY
(
SELECT Column1, Column2
FROM
(
SELECT Column1, Column2, ROW_NUMBER() OVER (ORDER BY Column1, Column2) AS rownumber
FROM data d1
WHERE d1.Row11 = d.Row11 AND d1.Row12 = d.Row12 AND d1.Row13 = d.Row13
) drows
WHERE rownumber = 2
) dsecond
CROSS APPLY
(
SELECT TOP 1 Column1, Column2
FROM data d1
WHERE d1.Row11 = d.Row11 AND d1.Row12 = d.Row12 AND d1.Row13 = d.Row13
ORDER BY 1 DESC,2 DESC
) dthird
Which gives the results:
/-----------------------------------------------------------------------------------\
| Row11 | Row12 | Row13 | Column1 | Column2 | Column1 | Column2 | Column1 | Column2 |
|-------+-------+-------+---------+---------+---------+---------+---------+---------|
| 1 | 5 | 4 | 1 | 4 | 2 | 5 | 3 | 6 |
| 2 | 20 | 25 | 3 | 6 | 7 | 8 | 9 | 5 |
\-----------------------------------------------------------------------------------/
It should then be relatively trivial to group this in the table in your SSRS report by Row11, Row12, Row13, placing the values for Row11, Row12 and Row13 into the Group Header row and the values for all 6 Column1 and Column2 values into the detail row:
Design:
Results:
Note: this only works for 3 (or fewer) pairs of Column1/Column2 values per tuple of Row11, Row12, Row13 values.

Related

Getting a lineage of linked rows with details

I'm trying to get a "lineage" or similar, and also information about the first and last links (at least; all would be good), out of a table that has self-referential links between rows that have been "replaced" and rows that have replaced them. The table has a structure along these lines:
CREATE TABLE Thing (
Id INT PRIMARY KEY,
TStamp DATETIME,
Replaces INT NULL,
ReplacedBy INT NULL
);
I'm stuck with this structure. :-) It's sort of doubly-linked (yes, it's a bit silly): Each row has a unique Id, and then a row that has been "replaced" by another will have a non-NULL ReplacedBy giving the Id of the replacement row, and the replacement row will also have a link back to what it replaces in Replaces. So we can use either Replaces or ReplacedBy (or both) if we like.
Here's some sample data:
INSERT INTO Thing
(Id, TStamp, Replaces, ReplacedBy)
VALUES
(1, '2017-01-01', NULL, 11),
(2, '2017-01-02', NULL, 12),
(3, '2017-01-03', NULL, NULL),
(4, '2017-01-04', NULL, NULL),
(11, '2017-01-11', 1, NULL),
(12, '2017-01-12', 2, 22),
(22, '2017-01-22', 12, NULL);
So 1 was replaced by 11, 2 was replaced by 12, and 12 was replaced by 22.
I'd like to get the following information for each chain of links from this table in a reasonable way:
Details of the row that started the chain
Details of the final row in the chain
Details of the links in-between or at least how many links (total) there are in the chain
...filtered by a date range applied to the last row in the chain.
In an ideal universe, I'd get back something like this:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−−−−−−+
| 1 | 11 | 1 | 2 | 2017−01−01 |
| 1 | 11 | 11 | 2 | 2017−01−11 |
| 2 | 22 | 2 | 3 | 2017−01−02 |
| 2 | 22 | 12 | 3 | 2017−01−12 |
| 2 | 22 | 22 | 3 | 2017−01−22 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−−−−−−+
So far I have this query, which I could post-process to get the above:
WITH Data AS (
SELECT Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
UNION ALL
SELECT Thing.Id, Thing.TStamp, Thing.Replaces, Thing.ReplacedBy, Depth + 1
FROM Data
JOIN Thing
ON Thing.Replaces = Data.Id
)
SELECT *
FROM Data
WHERE ReplacedBy IS NOT NULL OR Depth > 0
ORDER BY
Id, Depth;
That gives me:
+−−−−+−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−+
| Id | TStamp | Replaces | ReplacedBy | Depth |
+−−−−+−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−+
| 1 | 2017−01−01 | NULL | 11 | 0 |
| 2 | 2017−01−02 | NULL | 12 | 0 |
| 11 | 2017−01−11 | 1 | NULL | 1 |
| 12 | 2017−01−12 | 2 | 12 | 0 |
| 12 | 2017−01−12 | 2 | 12 | 1 |
| 22 | 2017−01−13 | 12 | NULL | 1 |
| 22 | 2017−01−13 | 12 | NULL | 2 |
+−−−−+−−−−−−−−−−−−+−−−−−−−−−−+−−−−−−−−−−−−+−−−−−−−+
And I could use something like this to figure out (for instance) the final row of each chain:
WITH Data AS (
SELECT Id, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
UNION ALL
SELECT Thing.Id, Thing.Replaces, Thing.ReplacedBy, Depth + 1
FROM Data
JOIN Thing
ON Thing.Replaces = Data.Id
),
MaxData AS (
SELECT Data.Id, Data.Depth
FROM Data
JOIN (
SELECT Id, MAX(Depth) AS MaxDepth
FROM Data
GROUP BY Id
) j ON data.Id = j.Id AND Data.Depth = j.MaxDepth
WHERE Depth > 0
)
SELECT *
FROM MaxData
ORDER BY
Id;
...which gives me:
+−−−−+−−−−−−−+
| Id | Depth |
+−−−−+−−−−−−−+
| 11 | 1 |
| 12 | 1 |
| 22 | 2 |
+−−−−+−−−−−−−+
...but I've lost the starting point and the points along the way.
I have the strong feeling I'm missing something really straight-forward — but clever — that would let me get this largely with the query rather than post-processing, some kind of join with a "min" and "max" query (but not like my one above). What would it be?
The table doesn't have any indexes on Replaces or ReplacedBy, but we could add any needed. The table is only lightly used (roughly 300k rows and probably only a couple of hundred updates/inserts a day).
I'm limited to SQL Server 2008 features.
Inspired by Gordon Linoff's answer and HABO's comment which highlighted something Gordon was doing that was critical, I:
Removed the SQL Server 2012+ FIRST_VALUE function, replacing it with a CROSS JOIN on an "overview" query of the data
Included the Links count in the overview query
Removed the reliance on t in Gordon's WHERE NOT EXISTS (SELECT 1 FROM Thing t2 WHERE t2.ReplacedBy = t.id), which (at last on SQL Server 2008) wasn't bound to anything
Filtered out rows that weren't replaced
Below, I also add the date filtering mentioned in the question
...filtered by a date range applied to the last row in the chain.
...which Gordon didn't cover at all, and changes our approach, but only in terms of the arrow of time.
So, first, without the date criteria, sticking fairly close to Gordon's answer:
WITH Data AS (
SELECT Id AS FirstId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
WHERE Replaces IS NULL AND ReplacedBy IS NOT NULL
UNION ALL
SELECT d.FirstId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth + 1
FROM Data d
JOIN Thing t ON t.Replaces = d.Id
),
Overview AS (
SELECT FirstId, MAX(Id) AS LastId, COUNT(*) AS Links
FROM Data
GROUP BY
FirstId
)
SELECT d.FirstId, o.LastId, d.Id, o.Links, d.Depth, d.TStamp
FROM Data d
CROSS APPLY (
SELECT LastId, Links
FROM Overview
WHERE FirstId = d.FirstId
) o
ORDER BY
d.FirstId, d.Depth
;
The critical parts of that are grabbing the seed Id as FirstId here:
SELECT Id AS FirstId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
WHERE Replaces IS NULL AND ReplacedBy IS NOT NULL
and then propagating it through the results of the recursive join:
SELECT d.FirstId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth + 1
FROM Data d
JOIN Thing t ON t.Replaces = d.Id
Just adding that to my original query gives us most of what I wanted. Then we add a second query to get the LastId for each FirstId (Gordon did it as a FIRST_VALUE over a partition, but I can't do that in SQL Server 2008) and using an overview query also lets me grab the number of links. We cross-apply that on the basis of the FirstId value to get the overall results I wanted.
The query above returns the following for the sample data:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | Depth | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| 1 | 11 | 1 | 2 | 0 | 2017-01-01 |
| 1 | 11 | 11 | 2 | 1 | 2017-01-11 |
| 2 | 22 | 2 | 3 | 0 | 2017-01-02 |
| 2 | 22 | 12 | 3 | 1 | 2017-01-12 |
| 2 | 22 | 22 | 3 | 2 | 2017-01-13 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
...e.g., exactly what I wanted, plus Depth if I want (so I know what order the intermediary links were in).
If we wanted to include rows that were never replaced, we'd just change
WHERE Replaces IS NULL AND ReplacedBy IS NOT NULL
to
WHERE Replaces IS NULL
Giving us:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | Depth | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| 1 | 11 | 1 | 2 | 0 | 2017-01-01 |
| 1 | 11 | 11 | 2 | 1 | 2017-01-11 |
| 2 | 22 | 2 | 3 | 0 | 2017-01-02 |
| 2 | 22 | 12 | 3 | 1 | 2017-01-12 |
| 2 | 22 | 22 | 3 | 2 | 2017-01-13 |
| 3 | 3 | 3 | 1 | 0 | 2017-01-03 |
| 4 | 4 | 4 | 1 | 0 | 2017-01-04 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
But we've ignored the date criteria required by the question:
...filtered by a date range applied to the last row in the chain.
To do that without building a massive temporary result set, we have to work backward: Instead of selecting the starting point (the first entry in a chain, Replaces IS NULL), we need to select the ending point (the last entry in a chain, ReplacedBy IS NULL), and then invert our logic working back through the chain. It's largely a matter of:
Swapping FirstId with LastId
Swapping Replaces with ReplacedBy (convenient the table had both!)
Using MIN to get the first ID in the chain rather than MAX to get the last
Using d.Depth - 1 rather than d.Depth + 1
Then fixing-up Depth based on Links once we know it in our final select, to get those nice values where 0 = first link rather than some varying negative number: o.Links + d.Depth - 1 AS Depth
All of which gives us:
WITH Data AS (
SELECT Id AS LastId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing
WHERE ReplacedBy IS NULL AND Replaces IS NOT NULL
-- Filtering by date of last entry would go here
UNION ALL
SELECT d.LastId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth - 1
FROM Data d
JOIN Thing t ON t.ReplacedBy = d.Id
),
Overview AS (
SELECT LastId, MIN(Id) AS FirstId, COUNT(*) AS Links
FROM Data
GROUP BY
LastId
)
SELECT o.FirstId, d.LastId, d.Id, o.Links, o.Links + d.Depth - 1 AS Depth, d.TStamp
FROM Data d
CROSS APPLY (
SELECT FirstId, Links
FROM Overview
WHERE LastId = d.LastId
) o
ORDER BY
o.FirstId, d.Depth
;
So for instance, if we used
AND TStamp BETWEEN '2017-01-12' AND '2017-02-01'
where I have
-- Filtering by date of last entry would go here
above, with our sample data we'd get this result:
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| FirstId | LastId | Id | Links | Depth | TStamp |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
| 2 | 22 | 2 | 3 | 0 | 2017−01−02 |
| 2 | 22 | 12 | 3 | 1 | 2017−01−12 |
| 2 | 22 | 22 | 3 | 2 | 2017−01−13 |
+−−−−−−−−−+−−−−−−−−+−−−−+−−−−−−−+−−−−−−−+−−−−−−−−−−−−+
...because the last link the Id = 1 chain is outside the date range, so we don't include it.
This is a little tricky. Arrange the CTE to start at the beginning of each list. That makes the subsequent processing easier:
WITH Data AS (
SELECT Id as FirstId, Id, TStamp, Replaces, ReplacedBy, 0 AS Depth
FROM Thing t
WHERE NOT EXISTS (SELECT 1 FROM Thing t2 WHERE t2.ReplacedBy = t.id)
UNION ALL
SELECT d.FirstId, t.Id, t.TStamp, t.Replaces, t.ReplacedBy, d.Depth + 1
FROM Data d JOIN
Thing t
ON t.Replaces = d.Id
)
SELECT d.*,
FIRST_VALUE(id) OVER (PARTITION BY FirstId ORDER BY Depth DESC) as LastId
FROM Data d;
Then, you can use FIRST_VALUE() with a reverse sort to get the last value in the chain.
This returns chains that have no links. You can add a filter to remove these.

Get next 2 values for sequence in single row

MSSQL 2014
I am performing a split on records in sql. The key of the table MyTable is generated by a sequence MySeq. Given a set of records, I want to generate 2 new keys that I can then do some work with then insert child rows.
MyTable
+----+
| Id |
+----+
| 1 |
+----+
| 2 |
Now to select my two new keys:
SELECT Id,
NEXT VALUE FOR MySeq AS ChildId1,
NEXT VALUE FOR MySeq AS ChildId2
FROM MyTable
I want:
+----+----------+----------+
| Id | ChildId1 | ChildId2 |
+----+----------+----------+
| 1 | 3 | 4 |
+----+----------+----------+
| 2 | 5 | 6 |
I get:
+----+----------+----------+
| Id | ChildId1 | ChildId2 |
+----+----------+----------+
| 1 | 3 | 3 |
+----+----------+----------+
| 2 | 4 | 4 |
I think the reason for the single run of the sequence per row has something to do with the design of the feature. For example, it looks like you can order the sequence runs separately from the SELECT.
I have a work around that is fine enough (update the table var after initial INSERT), but before I left it that way, I thought I would see if there is a more natural way to get the result I am looking for.
What you can do in this situation is make the sequence to increment by two
CREATE SEQUENCE MySeq AS 
INT
START WITH 0 
INCREMENT BY 2; 
and then do a:
SELECT Id,
NEXT VALUE FOR MySeq AS ChildId1,
1 + NEXT VALUE FOR MySeq AS ChildId2
FROM MyTable
I could be wrong (as my MSSQL experience is limited... I mainly use other SQL programs) but could you not simply solve this with a couple of selects within selects?
i.e.:
SELECT ID, ((SELECT MAX(ID) FROM MyTable)+ID) AS ChildId1,
((SELECT MAX(ID) FROM MyTable)+ID+1) AS ChildId2
FROM MyTable

Data visualization on a web app

I have a data that should be presented as a flexible table.
For example, this is how I save the data on my mysql server:
Column 1 | Column 2| Column 3| Column 4|Column 6|
------------|---------|---------|---------|--------|
UniqueValue1| value | value 1 | aaa | 1 |
UniqueValue2| value | value 1 | bbb | 2 |
UniqueValue3| value | value 3 | ccc | 3 |
UniqueValue4| value | value 2 | ddd | 4 |
Now, I want to present it on a web site with the ability to query this database.
For example
Querying all the data where you find the "value 1" on column 3 and group it by column 3. The result will be
Querying all the data where column 5 is bigger than 2 and group it by column 3 The result will be:
Please find below 2 queries for your questions.
1. SELECT column3,column1,column2,column4,column5 FROM <table_name> WHERE column3 = "value 1" GROUP BY column3,column1;
2. SELECT column3,column1,column2,column4,column5 FROM <table_name> WHERE column5 > 2 GROUP BY column3,column1;

TSQL Multiple column unpivot with named rows possible?

I know there are several unpivot / cross apply discussions here but I was not able to find any discussion that covers my problem. What I've got so far is the following:
SELECT Perc, Salary
FROM (
SELECT jobid, Salary_10 AS Perc10, Salary_25 AS Perc25, [Salary_Median] AS Median
FROM vCalculatedView
WHERE JobID = '1'
GROUP BY JobID, SourceID, Salary_10, Salary_25, [Salary_Median]
) a
UNPIVOT (
Salary FOR Perc IN (Perc10, Perc25, Median)
) AS calc1
Now, what I would like is to add several other columns, eg. one named Bonus which I also want to put in Perc10, Perc25 and Median Rows.
As an alternative, I also made a query with cross apply, but here, it seems as if you can not "force" sort the rows like you can with unpivot. In other words, I can not have a custom sort, but only a sort that is according to a number within the table, if I am correct? At least, here I do get the result like I wish to have, but the rows are in a wrong order and I do not have the rows names like Perc10 etc. which would be nice.
SELECT crossapplied.Salary,
crossapplied.Bonus
FROM vCalculatedView v
CROSS APPLY (
VALUES
(Salary_10, Bonus_10)
, (Salary_25, Bonus_25)
, (Salary_Median, Bonus_Median)
) crossapplied (Salary, Bonus)
WHERE JobID = '1'
GROUP BY crossapplied.Salary,
crossapplied.Bonus
Perc stands for Percentile here.
Output is intended to be something like this:
+--------------+---------+-------+
| Calculation | Salary | Bonus |
+--------------+---------+-------+
| Perc10 | 25 | 5 |
| Perc25 | 35 | 10 |
| Median | 27 | 8 |
+--------------+---------+-------+
Do I miss something or did I something wrong? I'm using MSSQL 2014, output is going into SSRS. Thanks a lot for any hint in advance!
Edit for clarification: The Unpivot-Method gives the following output:
+--------------+---------+
| Calculation | Salary |
+--------------+---------+
| Perc10 | 25 |
| Perc25 | 35 |
| Median | 27 |
+--------------+---------+
so it lacks the column "Bonus" here.
The Cross-Apply-Method gives the following output:
+---------+-------+
| Salary | Bonus |
+---------+-------+
| 35 | 10 |
| 25 | 5 |
| 27 | 8 |
+---------+-------+
So if you compare it to the intended output, you'll notice that the column "Calculation" is missing and the row sorting is wrong (note that the line 25 | 5 is in the second row instead of the first).
Edit 2: View's definition and sample data:
The view basically just adds computed columns of the table. In the table, I've got Columns like Salary and Bonus for each JobID. The View then just computes the percentiles like this:
Select
Percentile_Cont(0.1)
within group (order by Salary)
over (partition by jobID) as Salary_10,
Percentile_Cont(0.25)
within group (order by Salary)
over (partition by jobID) as Salary_25
from Tabelle
So the output is like:
+----+-------+---------+-----------+-----------+
| ID | JobID | Salary | Salary_10 | Salary_25 |
+----+-------+---------+-----------+-----------+
| 1 | 1 | 100 | 60 | 70 |
| 2 | 1 | 100 | 60 | 70 |
| 3 | 2 | 150 | 88 | 130 |
| 4 | 3 | 70 | 40 | 55 |
+----+-------+---------+-----------+-----------+
In the end, the view will be parameterized in a stored procedure.
Might this be your approach?
After your edits I understand, that your solution with CROSS APPLY would comes back with the right data, but not in the correct output. You can add constant values to your VALUES and do the sorting in a wrapper SELECT:
SELECT wrapped.Calculation,
wrapped.Salary,
wrapped.Bonus
FROM
(
SELECT crossapplied.*
FROM vCalculatedView v
CROSS APPLY (
VALUES
(1,'Perc10',Salary_10, Bonus_10)
, (2,'Perc25',Salary_25, Bonus_25)
, (3,'Median',Salary_Median, Bonus_Median)
) crossapplied (SortOrder,Calculation,Salary, Bonus)
WHERE JobID = '1'
GROUP BY crossapplied.SortOrder,
crossapplied.Calculation,
crossapplied.Salary,
crossapplied.Bonus
) AS wrapped
ORDER BY wrapped.SortOrder

Adding a row value more than once to STDEV()

I have the following table:
| rowNumber | amount | count |
| 1 | 1000 | 2 |
| 2 | 1500 | 3 |
| 3 | 1750 | 3 |
| 4 | 2000 | 1 |
Now if I want to get the stdev how can I make the amount of the row 1 be inserted in the function's expression twice, the amount of the row 2 be inserted 3 times and so on... Right now each amount is inserted in a temp table the necessary times and we get the stdev from that table, but I want to see if there is a better and more efficient way to do so.
Thanks.
You could join onto a numbers table
SELECT STDEV(amount)
FROM YourTable JOIN Numbers ON N <= YourTable.[count]
or write a custom CLR aggregate that takes both parameters and does the corresponding calculation.

Resources