Conditional rowcount - sql

Emplid FunctionId Count
1------- 2
1 ------ 3 ---------- 1
1 ------ 4 ---------- 2
1 ------ 4
1 ------ 5 ---------- 3
1 ------ 6 ---------- 4
1 ------ 3 ---------- 5
2 ------ 3
2 ------ 3
2 ------ 1 ---------- 1
2 ------ 2 ---------- 2
H&R is looking for a measure to count the flexibility/mobility in the company.
When an Employee is changing from job/function a FunctionID is stored in the DWH empl dim. See the example above how this table looks like (6000 employee records with lots of mutations as well).
So I need a count only when an employee is going to do something else (another function).
The example above is showing you how the count output should be.
How can do it with T-SqL or in a SSIS package (foreach loop ?)

Disclaimer
For given test script, SQL Server honors the order of inserts in the Employee CTE but beware that there is no guarantee it would do so every time.
It is imperative that you provide an additional sort order besides EmplId in the ROW_NUMBER statement.
SQL Statement
;WITH EmployeeRank AS (
SELECT EmplId
, FunctionID
, rn = ROW_NUMBER() OVER (ORDER BY EmplId)
FROM Employee
)
SELECT er1.EmplID, COUNT(*)
FROM EmployeeRank er1
INNER JOIN EmployeeRank er2
ON er2.Emplid = er1.Emplid
AND er2.rn = er1.rn + 1
AND er2.FunctionId <> er1.FunctionId
GROUP BY
er1.EmplID
Test script
;WITH Employee(Emplid, FunctionId) AS (
SELECT 1, 2
UNION ALL SELECT 1, 3 --1
UNION ALL SELECT 1, 4 --2
UNION ALL SELECT 1, 4
UNION ALL SELECT 1, 5 --3
UNION ALL SELECT 1, 6 --4
UNION ALL SELECT 1, 3 --5
UNION ALL SELECT 2, 3 --1
UNION ALL SELECT 2, 3
UNION ALL SELECT 2, 1 --2
UNION ALL SELECT 2, 2 --3
)
, EmployeeRank AS (
SELECT EmplId
, FunctionID
, rn = ROW_NUMBER() OVER (ORDER BY EmplId)
FROM Employee
)
SELECT er1.EmplID, COUNT(*)
FROM EmployeeRank er1
INNER JOIN EmployeeRank er2
ON er2.Emplid = er1.Emplid
AND er2.rn = er1.rn + 1
AND er2.FunctionId <> er1.FunctionId
GROUP BY
er1.EmplID

Related

Filter rows in Oracle select query (and not script or procedure)

I have code in my Oracle stored procedure like this:
SELECT COUNT(EMP_ID) INTO A_VARIABLE
FROM STUDENT;
IF (A_VARIABLE > 0) THEN
SELECT * FROM DEPARTMENT1;
ELSE
SELECT * FROM DEPARTMENT2;
END IF;
Basically I have to do this same filtration of rows in my SQL query (and not in the procedure or script).
Can anyone help with this Oracle query?
I have tried multiple solutions, but I'm not getting the desired output.
For 19c (19.7 and above) you may use SQL_MACRO(table) feature and inline function declaration.
But different output structure of the same query is somewhat misleading, so I would recommend to use union all approach and align columns of two tables.
Below is an example for SQL Macro.
Setup:
create table table1
as
select
level as id,
level as val
from dual
connect by level < 5
create table table2
as
select
level as id,
lpad(level, 4, '0') as val,
dbms_random.string('x', 5) as str2
from dual
connect by level < 4
create table decision
as
select 'Y' as res
from dual
Run 1:
with function f_decide_table
return varchar2
sql_macro(table)
as
l_res varchar2(100);
begin
select
coalesce (
max(q'[select * from table1]'),
q'[select * from table2]'
)
into l_res
from decision;
return l_res;
end;
select *
from f_decide_table()
order by 1
ID
VAL
1
1
2
2
3
3
4
4
Run 2:
truncate table decision
with function f_decide_table
return varchar2
sql_macro(table)
as
l_res varchar2(100);
begin
select
coalesce (
max(q'[select * from table1]'),
q'[select * from table2]'
)
into l_res
from decision;
return l_res;
end;
select *
from f_decide_table()
order by 1
ID
VAL
STR2
1
0001
DGNY9
2
0002
UHFYH
3
0003
EU12B
fiddle
Sample student table; depending on course students take, main query will return data from one table (or another).
SQL> select * from student;
ID NAME COURSE
1 Little Web
2 Foot Web
SQL> select * from department1;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL> select * from department2;
DEPTNO DNAME LOC
---------- -------------- -------------
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Query uses a CTE that counts rows in student table; it is then used in exists subquery for two similar select statements which fetch rows from one of departments tables.
Currently, nobody is in IT so departments2 table it is:
SQL> with temp as
2 (select count(*) cnt
3 from student
4 where course = 'IT')
5 --
6 select * from department1
7 where exists (select null from temp
8 where cnt > 0
9 )
10 union all
11 select * from department2
12 where exists (select null from temp
13 where cnt = 0
14 );
DEPTNO DNAME LOC
---------- -------------- -------------
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
However, if someone studies IT, department1 will provide data:
SQL> update student set course = 'IT' where id = 1;
1 row updated.
SQL> select * from student;
ID NAME COURSE
---------- ------ ----------
1 Little IT
2 Foot Web
SQL> with temp as
2 (select count(*) cnt
3 from student
4 where course = 'IT')
5 --
6 select * from department1
7 where exists (select null from temp
8 where cnt > 0
9 )
10 union all
11 select * from department2
12 where exists (select null from temp
13 where cnt = 0
14 );
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL>
Closest thing to what you want to do (using just sql) would mean that you need to know all the column names and datatypes in both tables.
Next, you would have to do the datatype conversion for one union query as you have to select same number of columns with same datatypes.
The result would have all that you need but with some empty columns (those that do exist in other table but not in one you are selecting from).
Depending on total number of columns and on differences in names and types this coulld becomee too complicated and you realy shoud think about some function and reference queries.
Anyway, lets create some sample data to work with:
WITH -- sample data
students (ID, A_NAME) AS
(
Select 1, 'Mark' From Dual
),
dept_1 (ID, DEPT_NAME, DEPT_CITY) AS
(
Select '10', 'HR', 'NEW_YORK' From Dual Union All
Select '20', 'IT', 'BOSTON' From Dual
),
dept_2 (ID, DEPARTMENT) AS
(
Select 30, 'SALES' From Dual Union All
Select 40, 'PRODUCTION' From Dual
),
-- dept_1 has 3 columns (ID VARCHAR) - same column name different datatype
-- dept_2 has 2 columns (ID NUMBER)
-- The rest of the columns have different names and same datatype
create cte (namings) - forcing union of rows for all columns from both tables - dummy filling non existant and converting To_Char(ID) to get same datatype (this is needed for union to work)
-- here are total of 5 columns coded - it can be widen more if needed, this is just a sample to show handling of extra columns
namings AS
(
SELECT ROWNUM "RN", 'dept_1' "TABLE_NAME", Count(*) "CNT", To_Char(ID) "V1", DEPT_NAME "V2", DEPT_CITY "V3", 'X' "V4", 'X' "V5", 'ID' "C1", 'DEPT_NAME' "C2", 'DEPT_CITY' "C3", 'COL_X4' "C4", 'COL_X5' "C5"
From dept_1
Where CASE WHEN (Select Count(*) "CNT" From students Where ID = 999) > 0 THEN 1 ELSE 0 END = 1
Group By ROWNUM, ID, DEPT_NAME, DEPT_CITY
Union All
SELECT ROWNUM "RN", 'dept_2' "TABLE_NAME", Count(*) "CNT", To_Char(ID) "V1", DEPARTMENT "V2", 'X' "V3", 'X' "V4", 'X' "V5", 'ID' "C1", 'DEPARTMENT' "C2", 'COL_X3' "C3", 'COL_X4' "C4", 'COL_X5' "C5"
From dept_2
Where CASE WHEN (Select Count(*) "CNT" From students Where ID = 999) > 0 THEN 1 ELSE 0 END = 0
Group By ROWNUM,ID, DEPARTMENT
),
R e s u l t when count from students = 0 (filtered out with where clause)
RN TABLE_NAME CNT V1 V2 V3 V4 V5 C1 C2 C3 C4 C5
---------- ---------- ---------- ---------------------------------------- ---------- -------- -- -- -- ---------- --------- ------ ------
2 dept_2 1 40 PRODUCTION X X X ID DEPARTMENT COL_X3 COL_X4 COL_X5
1 dept_2 1 30 SALES X X X ID DEPARTMENT COL_X3 COL_X4 COL_X5
R e s u l t when count from students > 0
RN TABLE_NAME CNT V1 V2 V3 V4 V5 C1 C2 C3 C4 C5
---------- ---------- ---------- ---------------------------------------- ---------- -------- -- -- -- ---------- --------- ------ ------
1 dept_1 1 10 HR NEW_YORK X X ID DEPT_NAME DEPT_CITY COL_X4 COL_X5
2 dept_1 1 20 IT BOSTON X X ID DEPT_NAME DEPT_CITY COL_X4 COL_X5
create another cte (transposed) to unpivot the above data
transposed AS
( SELECT * FROM namings
UNPIVOT ( (NAMES, VALS) For COL_NAME IN( (C1, V1), (C2, V2), (C3, V3), (C4, V4), (C5, V5) ) )
WHERE VALS <> 'X'
)
R e s u l t when count from students = 0
RN TABLE_NAME CNT COL_NAME NAMES VALS
---------- ---------- ---------- -------- ---------- ----------------------------------------
2 dept_2 1 C1_V1 ID 40
2 dept_2 1 C2_V2 DEPARTMENT PRODUCTION
1 dept_2 1 C1_V1 ID 30
1 dept_2 1 C2_V2 DEPARTMENT SALES
R e s u l t when count from students > 0
RN TABLE_NAME CNT COL_NAME NAMES VALS
---------- ---------- ---------- -------- ---------- ----------------------------------------
1 dept_1 1 C1_V1 ID 10
1 dept_1 1 C2_V2 DEPT_NAME HR
1 dept_1 1 C3_V3 DEPT_CITY NEW_YORK
2 dept_1 1 C1_V1 ID 20
2 dept_1 1 C2_V2 DEPT_NAME IT
2 dept_1 1 C3_V3 DEPT_CITY BOSTON
Main sql - pivoting data to columns from both tables
SELECT DISTINCT
RN,
TABLE_NAME,
MAX(ID_V) OVER(Partition By RN) "ID",
MAX(DEPT_NAME_V) OVER(Partition By RN) "DEPT_NAME",
MAX(DEPT_CITY_V) OVER(Partition By RN) "DEPT_CITY",
MAX(DEPARTMENT_V) OVER(Partition By RN) "DEPARTMENT"
FROM (
SELECT * FROM transposed
PIVOT (
COUNT(NAMES), MAX(VALS) "V" FOR NAMES IN('ID' "ID", 'DEPT_NAME' "DEPT_NAME", 'DEPT_CITY' "DEPT_CITY", 'DEPARTMENT' "DEPARTMENT")
)
)
ORDER BY RN
R e s u l t when count from students = 0
RN TABLE_NAME ID DEPT_NAME DEPT_CITY DEPARTMENT
---------- ---------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
1 dept_2 30 SALES
2 dept_2 40 PRODUCTION
R e s u l t when count from students > 0
RN TABLE_NAME ID DEPT_NAME DEPT_CITY DEPARTMENT
---------- ---------- ---------------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
1 dept_1 10 HR NEW_YORK
2 dept_1 20 IT BOSTON

Calculation of distinct and sum

I have a a below table where all the columns are same except for group column and I am calculating count(distinct group) and blocks in the same table:
Input:
id
time
CODE
group
value
total_blocks
1
22
32206
mn2
1
200
1
22
32206
mn4
1
200
Output:
id
time
CODE
group
value
count(distinct group)
blocks
1
22
32206
mn2
1
2
100
1
22
32206
mn4
1
2
100
count(distinct group) is just distinct values (mn2 and mn4) and blocks overall wrt to code(32206) is 200, but I am splitting the same over the two rows.
The output should look exactly the same in the final, without removal of any columns.
I tried using count(distinct) but it didn't work
Oracle:
Try it like here:
WITH -- S a m p l e d a t a
tbl (ID, A_TIME, CODE, A_GROUP, A_VALUE, TOTAL_BLOCKS) AS
(
Select 1, 22, 32206, 'mn2', 1, 200 From Dual Union All
Select 1, 22, 32206, 'mn4', 1, 200 From Dual
)
-- S Q L --
Select
ID, A_TIME, CODE, A_GROUP, A_VALUE,
Count(DISTINCT A_GROUP) OVER(Partition By CODE) "COUNT_DIST_GROUP", -- Count distinct groups per CODE
-- Count(DISTINCT A_GROUP) OVER() "COUNT_DIST_GROUP", --Count distinct groups over the whole table
TOTAL_BLOCKS / Count(*) OVER(Partition By CODE) "BLOCKS" -- TOTAL_BLOCKS divided by number of rows per CODDE
-- TOTAL_BLOCKS / Count(*) OVER() "BLOCKS" -- TOTAL_BLOCKS divided by number of rows in the whole table
From
tbl
R e s u l t :
ID A_TIME CODE A_GROUP A_VALUE COUNT_DIST_GROUP BLOCKS
---------- ---------- ---------- ------- ---------- ---------------- ----------
1 22 32206 mn2 1 2 100
1 22 32206 mn4 1 2 100
Comments in code explain the basic use of Count() Over() analytic function. More about analytic functions here.
Using just ROW_NUMBER() analytic function and Max() aggregate function...
-- S Q L --
Select
r.ID, r.A_TIME, r.CODE, t.A_GROUP, r.A_VALUE, MAX_RN "COUNT_DIST_GROUP", (TOTAL_BLOCKS / MAX_RN) "BLOCKS"
From
( SELECT ID, A_TIME, CODE, A_VALUE, Max(RN) "MAX_RN"
FROM (Select ID, A_TIME, CODE, A_VALUE, Row_Number() OVER(Partition By CODE Order By CODE, A_GROUP) "RN"
From tbl )
GROUP BY ID, A_TIME, CODE, A_VALUE ) r
Inner Join tbl t ON(t.CODE = r.CODE)
ID A_TIME CODE A_GROUP A_VALUE COUNT_DIST_GROUP BLOCKS
---------- ---------- ---------- ------- ---------- ---------------- ----------
1 22 32206 mn2 1 2 100
1 22 32206 mn4 1 2 100
... and it works with another group of similar data:
WITH -- S a m p l e d a t a
tbl (ID, A_TIME, CODE, A_GROUP, A_VALUE, TOTAL_BLOCKS) AS
(
Select 1, 22, 32206, 'mn2', 1, 200 From Dual Union All
Select 1, 22, 32206, 'mn4', 1, 200 From Dual Union All
--
Select 1, 22, 32207, 'mn6', 1, 450 From Dual Union All
Select 1, 22, 32207, 'mn7', 1, 450 From Dual Union All
Select 1, 22, 32207, 'mn8', 1, 450 From Dual
)
-- S Q L --
Select
r.ID, r.A_TIME, r.CODE, t.A_GROUP, r.A_VALUE, MAX_RN "COUNT_DIST_GROUP", (TOTAL_BLOCKS / MAX_RN) "BLOCKS"
From
( SELECT ID, A_TIME, CODE, A_VALUE, Max(RN) "MAX_RN"
FROM (Select ID, A_TIME, CODE, A_VALUE, Row_Number() OVER(Partition By CODE Order By CODE, A_GROUP) "RN"
From tbl )
GROUP BY ID, A_TIME, CODE, A_VALUE ) r
Inner Join tbl t ON(t.CODE = r.CODE)
ID A_TIME CODE A_GROUP A_VALUE COUNT_DIST_GROUP BLOCKS
---------- ---------- ---------- ------- ---------- ---------------- ----------
1 22 32206 mn2 1 2 100
1 22 32206 mn4 1 2 100
1 22 32207 mn6 1 3 150
1 22 32207 mn7 1 3 150
1 22 32207 mn8 1 3 150

Query to display one to many result in a single table

Ive been trying to use the GROUP function and also PIVOT but I cannot wrap my head around how to merge these tables and combine duplicate rows. Currently my SELECT statement returns results with duplicate UserID rows but I want to consolidate them into columns.
How would I join TABLE1 and TABLE2 into a new table which would look something like this:
NEW TABLE:
UserID Username ParentID 1 ParentID 2
--------- -------- -------- ----------
1 Dave 1 2
2 Sally 3 4
TABLE1:
UserID Username ParentID
--------- -------- --------
1 Dave 1
1 Dave 2
2 Sally 3
2 Sally 4
Table 2:
ParentID Username
--------- --------
1 Sarah
2 Joe
3 Tom
4 Mark
O r a c l e
The with clause is here just to generate some sample data and, as such, it is not a part of the answer.
After joining the tables you can use LAST_VALUE analytic function with windowing clause to get the next PARENT_ID of the user. That column (PARENT_ID_2) contains a value only within the first row of a particular USER_ID (ROW_NUMBER analytic function). Afterwords just filter out rows where PARENT_ID_2 is empty...
Sample data:
WITH
tbl_1 AS
(
Select 1 "USER_ID", 'Dave' "USER_NAME", 1 "PARENT_ID" From Dual Union All
Select 1 "USER_ID", 'Dave' "USER_NAME", 2 "PARENT_ID" From Dual Union All
Select 2 "USER_ID", 'Sally' "USER_NAME", 3 "PARENT_ID" From Dual Union All
Select 2 "USER_ID", 'Sally' "USER_NAME", 4 "PARENT_ID" From Dual
),
tbl_2 AS
(
Select 1 "PARENT_ID", 'Sarah' "USER_NAME" From Dual Union All
Select 2 "PARENT_ID", 'Joe' "USER_NAME" From Dual Union All
Select 3 "PARENT_ID", 'Tom' "USER_NAME" From Dual Union All
Select 4 "PARENT_ID", 'Mark' "USER_NAME" From Dual
)
Main SQL:
SELECT
*
FROM (
SELECT
t1.USER_ID "USER_ID",
t1.USER_NAME "USER_NAME",
t1.PARENT_ID "PARENT_ID_1",
CASE
WHEN ROW_NUMBER() OVER(PARTITION BY t1.USER_ID ORDER BY t1.USER_ID) = 1
THEN LAST_VALUE(t1.PARENT_ID) OVER(PARTITION BY t1.USER_ID ORDER BY t1.USER_ID ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING)
END "PARENT_ID_2"
FROM
tbl_1 t1
INNER JOIN
tbl_2 t2 ON(t1.PARENT_ID = t2.PARENT_ID)
)
WHERE PARENT_ID_2 Is Not Null
... and the Result ...
-- USER_ID USER_NAME PARENT_ID_1 PARENT_ID_2
-- ---------- --------- ----------- -----------
-- 1 Dave 1 2
-- 2 Sally 3 4
The windowing clause in this answer
ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING
takes curent and next row and returns the value defined by the analytic function (LAST_VALUE) taking care of grouping (PARTITION BY) and ordering of the rows. Regards...
This is mySql ver 5.6. Create a concatenated ParentID using group concat then separate the concatenated ParentID (1,2) and (3,4) into ParentID 1 and Parent ID 2.
SELECT t1.UserID,
t1.Username,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(t1.ParentID), ',', 1), ',', -1) AS `ParentID 1`,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(t1.ParentID), ',', 2), ',', -1) as `ParentID 2`
FROM TABLE1 t1
INNER JOIN TABLE2 t2 on t1.ParentID = t2.ParentID
GROUP BY t1.UserID
ORDER BY t1.UserID;
Result:
UserID Username ParentID 1 ParentID 2
1 Dave 1 2
2 Sally 3 4

SQL Server Select Get Single Row From Another Table

Needing some help with SQL Server select query here.
I have the following tables defined:
UserSource
UserSourceID ID Name Dept SourceID
1 1 John AAAA 1
2 1 John AAAA 2
3 2 Nena BBBB 1
4 2 Nena BBBB 2
5 3 Gord AAAA 2
6 3 Gord AAAA 1
7 4 Stan CCCC 3
Source
SourceID Description RankOrder
1 FromHR 1
2 FromTemp 2
3 Others 3
Need to join both tables and select only the row where the rank is the smallest. Such that the resulting row would be:
UserSourceID ID Name Dept SourceID Description RankOrder
1 1 John AAAA 1 FromHR 1
3 2 Nena BBBB 1 FromHR 1
6 3 Gord AAAA 1 FromHR 1
7 4 Stan CCCC 3 Others 3
TIA.
Edit:
Here's what I have come up so far, but I seem to be missing something:
WITH
TableA AS(
SELECT 1 AS UserSourceID, 1 AS ID, 'John' AS [Name], 'AAAA' as [Dept], 1 as SourceID
UNION SELECT 2, 1, 'John', 'AAAA', 2
UNION SELECT 3, 2, 'Nena', 'BBBB', 1
UNION SELECT 4, 2, 'Nena', 'BBBB', 2
UNION SELECT 5, 3, 'Gord', 'AAAA', 2
UNION SELECT 6, 3, 'Gord', 'AAAA', 1
UNION SELECT 7, 4, 'Stan', 'DDDD', 3)
,
TableB AS(
SELECT 1 as SourceID, 'FromHR' as [Description], 1 as RankOrder
UNION SELECT 2, 'FromTemp', 2
UNION SELECT 3, 'Others', 3
)
SELECT DISTINCT tblA.*, tblB.SourceID, tblB.Description
FROM TableB tblB
JOIN TableA tblA ON tblA.SourceID = tblB.SourceID
LEFT JOIN TableB b2 ON b2.SourceID = tblB.SourceID
AND B2.RankOrder < tblB.RankOrder
WHERE B2.SourceID IS NULL
UPDATE:
I scanned the tables and there might be some variations of data. I have updated the data for the question as above.
Practically, I need to join these two tables, and be able to only select the row which would have the least RankOrder. In case of record UserSourceID = 7, that particular record would be selected because there's only one row that exists after the tables have been joined.
I use windowed aggregates for this type of solution pretty regularly. ROW_NUMBER will order and number the rows based on the PARTITION and ORDER you specify in the OVER clause.
select UserSoruceID
, ID
, Name
, Dept
, SourceID
, Description
, RankOrder
FROM (SELECT UserSoruceID
, ID
, Name
, Dept
, u.SourceID
, Description
, RankOrder
, ROW_NUMBER() over(PARTITION BY ID ORDER BY RankOrder) ranknum
FROM UserSource u
INNER JOIN
Source s
on s.SourceID = u.SourceID ) a
WHERE ranknum = 1
So in this case, for every ID, number the rows based on RankOrder, and then filter where so you only view the first row.
Here's a helpful link to that function from Microsoft. ROW_NUMBER
----UPDATE----
Here's with Rank and Row Number as options.
select UserSoruceID
, ID
, Name
, Dept
, SourceID
, Description
, RankOrder
FROM (SELECT UserSoruceID
, ID
, Name
, Dept
, u.SourceID
, Description
, RankOrder
, ROW_NUMBER() over(PARTITION BY ID ORDER BY RankOrder) row_num
, RANK() over(PARTITION BY ID ORDER BY RankOrder) rank_num --use this if you want to see the duplicate records
FROM UserSource u
INNER JOIN
Source s
on s.SourceID = u.SourceID ) a
WHERE row_num = 1 --rank_num = 1
Replace row_num with rank_num to view any items with duplicate RankOrder entries

Projection/Replication in SQL Query?

My SQL is a bit rusty -- is there a SQL way to project an input table that looks something like this:
Name SlotValue Slots
---- --------- -----
ABC 3 1
ABC 4 2
ABC 6 5
Into a 'projected' result table that looks like this:
Name SlotSum Slot
---- ------- ----
ABC 13 1
ABC 10 2
ABC 6 3
ABC 6 4
ABC 6 5
In other words, the result set should contain a number of rows equal to MAX(Slots), enumerated (Slot) from 1 to MAX(Slots), and Sum for each of these 'slots' should reflect the sum of the SlotValues projected out to the 'Slots' position. for the pathological case:
Name SlotValue Slots
---- --------- -----
ABC 4 3
we should get:
Name SlotSum Slot
---- ------- ----
ABC 4 1
ABC 4 2
ABC 4 3
The summation logic is pretty straightforward -- project each SlotValue out to the number of Slots:
SlotValue SlotValue SlotValue Slot Sum
--------- --------- --------- ---- ---
3 4 6 1 13 (3+4+6)
0 4 6 2 10 (0+4+6)
0 0 6 3 6 (0+0+6)
0 0 6 4 6 (0+0+6)
0 0 6 5 6 (0+0+6)
UPDATE: In the end I used a variant of LOCALGHOST's approach in a stored proc. I was hoping there might be a way to do this without a loop.
I'm not sure you'll be able to do this in a view. You'd have to use a procedure.
You could make ProjectedTable a temporary/variable table in the procedure as well. I'd be really interested to see how you'd get this into a view because you need to dynamically generate a range of numbers.
declare #maxSlot int
set #maxSlot = select max(slots) from SlotTable
truncate ProjectedTable
while #i > 0
begin
insert into ProjectedTable (
SlotSum
,Slot
) values (
(select sum(slotValue) from SlotTable where slots >= #maxSlot)
,#maxSlot
)
set #maxSlot = #maxSlot - 1
end
select SlotSum, Slot from ProjectedTable
Here you go. This will do up to 100 slots in its current form. You can use your imagination to accommodate more.
DECLARE #SLOT TABLE
(
SlotName varchar(25) NOT NULL,
SlotValue int NOT NULL,
Slot int NOT NULL
)
INSERT INTO #SLOT (SlotName, SlotValue, Slot)
SELECT 'ABC', 3, 1
UNION
SELECT 'ABC', 4, 2
UNION
SELECT 'ABC', 6, 5
SELECT
CASE
WHEN SLOT.SlotName IS NOT NULL THEN SLOT.SlotName
ELSE
COALESCE(
(SELECT TOP 1 SL.SlotName FROM #SLOT AS SL WHERE SL.Slot < SLOT_PROJECT.Slot ORDER BY SL.Slot DESC),
(SELECT TOP 1 SL.SlotName FROM #SLOT AS SL WHERE SL.Slot > SLOT_PROJECT.Slot ORDER BY SL.Slot ASC)
)
END AS SlotName,
(
SELECT
SUM(SLOT10.SlotValue)
FROM
#SLOT AS SLOT10
WHERE
SLOT10.Slot >= SLOT_PROJECT.Slot
) AS SlotSum,
SLOT_PROJECT.Slot
FROM
(
SELECT
(TENS.Seq + ONES.Seq) AS Slot
FROM
(
SELECT 1 AS Seq
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
) AS ONES
CROSS JOIN
(
SELECT 0 AS Seq
UNION ALL
SELECT 10
UNION ALL
SELECT 20
UNION ALL
SELECT 30
UNION ALL
SELECT 40
UNION ALL
SELECT 50
UNION ALL
SELECT 60
UNION ALL
SELECT 70
UNION ALL
SELECT 80
UNION ALL
SELECT 90
) AS TENS
WHERE
(TENS.Seq + ONES.Seq) <= (SELECT MAX(Slot) FROM #SLOT)
) AS SLOT_PROJECT
LEFT JOIN #SLOT AS SLOT ON
SLOT.Slot = SLOT_PROJECT.Slot