convert the input table to mentioned output table by using SQL - sql

I want to convert the input table to mentioned output table by using SQL statement, Can anyone please help me on this.
Input table
+-------------+-----------+----------+
| start_value | end_value | interval |
+-------------+-----------+----------+
| 0 | 120 | 10 |
| 1 | 150 | 50 |
+-------------+-----------+----------+
OUTPUT
+-----------------+-----------+------------------+
| start_value | end_value | next_start_value |
+-----------------+-----------+------------------+
| 0 | 120 | 0 |
| 0 | 120 | 10 |
| 0 | 120 | 20 |
| 0 | 120 | 30 |
+-----------------+-----------+------------------+

SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE input ( start_value, end_value, "INTERVAL" ) AS
SELECT 0, 120, 10 FROM DUAL UNION ALL
SELECT 1, 150, 50 FROM DUAL;
Query 1:
WITH output ( rn, start_value, end_value, "INTERVAL", next_start_value ) AS (
SELECT ROWNUM, i.*, start_value FROM input i
UNION ALL
SELECT rn,
start_value,
end_value,
"INTERVAL",
next_start_value + "INTERVAL"
FROM output
WHERE next_start_value + "INTERVAL" <= end_value
)
SELECT start_value, end_value, next_start_value
FROM output
ORDER BY rn, next_start_value
Results:
| START_VALUE | END_VALUE | NEXT_START_VALUE |
|-------------|-----------|------------------|
| 0 | 120 | 0 |
| 0 | 120 | 10 |
| 0 | 120 | 20 |
| 0 | 120 | 30 |
| 0 | 120 | 40 |
| 0 | 120 | 50 |
| 0 | 120 | 60 |
| 0 | 120 | 70 |
| 0 | 120 | 80 |
| 0 | 120 | 90 |
| 0 | 120 | 100 |
| 0 | 120 | 110 |
| 0 | 120 | 120 |
| 1 | 150 | 1 |
| 1 | 150 | 51 |
| 1 | 150 | 101 |

As MTD's answer does not actually fully meet the expected output structure, based on his/hers approach, I'd suggest this altered answer:
WITH output (start_value, end_value, "INTERVAL", next_start_value) AS (
SELECT start_value, end_value, "INTERVAL", start_value AS next_start_value
FROM input
UNION ALL
SELECT start_value,
end_value,
"INTERVAL",
next_start_value + "INTERVAL" AS next_start_value
FROM output
WHERE next_start_value + "INTERVAL" <= end_value
)
SELECT start_value, end_value, next_start_value
FROM output
ORDER BY 1, 2, 3;
EDIT: Adding the query result:
START_VALUE | END_VALUE | NEXT_START_VALUE
------------+-----------+-----------------
0 | 120 | 0
0 | 120 | 10
0 | 120 | 20
0 | 120 | 30
0 | 120 | 40
0 | 120 | 50
0 | 120 | 60
0 | 120 | 70
0 | 120 | 80
0 | 120 | 90
0 | 120 | 100
0 | 120 | 110
0 | 120 | 120
1 | 150 | 1
1 | 150 | 51
1 | 150 | 101
This is the SQL Fiddle for it.

Related

Subtract values in each row of a column based on a WHERE and GROUP BY statement in SQL

I would like to subtract each row "Value" with the "Value" where Sub1=0 grouping by ID_1 and ID_2 using a SQL query.
This is the table structure:
------------------------------------
ID_1 |ID_2 | sub1 | Value
------------------------------------
1 | a | 0 | 20
1 | a | 50 | 30
1 | a | 100 | 40
1 | b | 0 | 25
1 | b | 50 | 30
1 | b | 100 | 50
2 | a | 0 | 5
2 | a | 50 | 10
2 | a | 100 | 30
2 | b | 0 | 25
2 | b | 50 | 50
2 | b | 100 | 70
I would like to group by ID_1 and ID_2 and subtract each row's value with the value where the Sub1=0
Output table should be :
------------------------------------
ID_1 |ID_2 | sub1 | Value | Diff
------------------------------------
1 | a | 0 | 20 | 0
1 | a | 50 | 30 | 10
1 | a | 100 | 40 | 20
1 | b | 0 | 25 | 0
1 | b | 50 | 30 | 5
1 | b | 100 | 50 | 25
2 | a | 0 | 5 | 0
2 | a | 50 | 10 | 5
2 | a | 100 | 30 | 25
2 | b | 0 | 25 | 0
2 | b | 50 | 50 | 25
2 | b | 100 | 70 | 45
Use a window function:
select t.*,
(value -
sum(case when sub1 = 0 then value else 0 end) over (partition by id_1, id_2)
) as diff
from t;
This should work:
select t1.*, t1.value - t2.value as diff
from t t1
left join t t2 on t2.id_1 = t1.id_1 and t2.id_2 = t1.id_2 and t2.sub1 = 0
See it here:
http://sqlfiddle.com/#!9/cab4d5/1

SQL - Need return from single table of SUMs over 3 different date ranges

I have a table of Account Transactions that includes ID, Amount, Date. Basically, I want to create a resulting table that looks at the table and returns what the SUM was for the Account over three different Ending Date Ranges. Then I want to Flag (Combined_Flag) each Account ID, 1 if any of the SUMs for that ID are non-zero, and a 0 if all of the SUMs are 0.
Date Range 1) Min Date to End of Last Month (-1 Month)
Date Range 2) Min Date to End of 2 Months ago (-2 Months)
Date Range 3) Min Date to End of Last Month, Last Year (-13 Months)
The Resulting table should be: ID, SUM_R1, SUM_R2, SUM_R3, Flag_R1, Flag_R2, Flag_R3, Combined_Flag
Example Data
| ID | Amount | Date |
| -------- | -------------- |-------------- |
| 1 | 20 | 09/01/19 |
| 2 | 40 | 09/01/19 |
| 3 | 0 | 09/01/19 |
| 4 | 0 | 09/01/19 |
| 1 | 10 | 10/01/19 |
| 2 | 0 | 10/01/19 |
| 3 | 0 | 10/01/19 |
| 4 | 0 | 10/01/19 |
| 1 | 15 | 11/01/19 |
| 2 | 40 | 11/01/19 |
| 3 | 0 | 11/01/19 |
| 4 | 0 | 11/01/19 |
| 1 | 20 | 09/01/20 |
| 2 | 40 | 09/01/20 |
| 3 | 0 | 09/01/20 |
| 4 | 50 | 09/01/20 |
| 1 | 10 | 10/01/20 |
| 2 | 0 | 10/01/20 |
| 3 | 0 | 10/01/20 |
| 4 | 65 | 10/01/20 |
| 1 | 15 | 11/01/20 |
| 2 | 40 | 11/01/20 |
| 3 | 0 | 11/01/20 |
| 4 | 0 | 11/01/20 |
Expected Result Table (Using Date of 12/21/2020)
| ID | SUM_R1 | SUM_R2 | SUM_R3 | Flag_R1 | Flag_R2 | Flag_R3 | Combined_Flag |
| -------- | -------- | -------- | -------- | --------- | --------- | --------- | --------------- |
| 1 | 90 | 75 | 45 | 1 | 1 | 1 | 1 |
| 2 | 160 | 120 | 80 | 1 | 1 | 1 | 1 |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 115 | 115 | 0 | 1 | 1 | 0 | 1 |
The difficulty I'm having is in joining the table basically to itself 2 times. I'm getting results all over the place and not really sure exactly what's going on.
Is this what you want?
select id,
sum(case when date < datefromparts(year(v.dte), month(v.dte), 1)
then amount else 0
end) as sum_r1,
sum(case when date < dateadd(month, -1, datefromparts(year(v.dte), month(v.dte), 1))
then amount else 0
end) as sum_r2,
sum(case when date < dateadd(month, -13, datefromparts(year(v.dte), month(v.dte), 1))
then amount else 0
end) as sum_r3,
max(case when amount > 0 and date < datefromparts(year(v.dte), month(v.dte), 1)
then 1 else 0
end) as flag_r1,
max(case when amount > 0 and date < dateadd(month, -1, datefromparts(year(v.dte), month(v.dte), 1))
then 1 else 0
end) as flag_r2,
max(case when amount > 0 and date < dateadd(month, -13, datefromparts(year(v.dte), month(v.dte), 1))
then 1 else 0
end) as flag_r3
from t cross join
(values (convert(date, '2020-12-21'))
) v(dte)
group by id;
The flag columns assume that the amounts are never negative (which is consistent with the data in your question.
EDIT:
The shorthand in the comment for creating the flag looks like:
abs(sign(sum(case when amount > 0 and date < datefromparts(year(v.dte), month(v.dte), 1)
then amount else 0
end))) as flag_r1,
Here is a db<>fiddle.

How can i create duplicate records based the value in another table

I have two tables in my database, Work_Order table which is the source table where work order information's stored i also have Work_Schedule table which contains work schedules which tell peoples in the production floor what to build, when and how much to build.
Work_Order Table looks like
Work order ItemCode Size Qty Qty_per_HR
41051 600111 14L-16.1 55 10
I want duplicate the above work order line in work order table above based on the Qty per hour and automatically create a work scheduler as shown below.
where TARGET = Work_Order.Qty/Work_Order.Qty_per_HR
Work_Schedule Table
Id Start Date/Time End Date/Time Work Order Work Center TARGET ACTUAL
1001 2019-07-22 7:00AM 2019-07-22 8:00AM 41051 1 10
1001 2019-07-22 8:00AM 2019-07-22 9:00AM 41051 1 10
1001 2019-07-22 9:00AM 2019-07-22 10:00AM 41051 1 10
1001 22019-07-22 10:15AM 2019-07-22 11:00AM 41051 1 10
1001 22019-07-22 11:00AM 2019-07-22 12:00PM 41051 1 10
1001 2019-07-22 1:30PM 2019-07-22 2:30PM 41051 1 5
My plan is to use AfterInsert trigger as soon as the user a work order create the duplicates.
Schedule windows
This seems like a natural for a recursive CTE:
with cte as (
select convert(datetime, '2019-07-22 7:00AM') as dt, workorder, 1 as workcenter, qtyperh as target,
itemcode, size, (qty - qtyperh) as qty, qtyperh
from t
union all
select dateadd(hour, 1, dt), workorder, workcenter,
(case when qty > qtyperh then qtyperh else qty end) as target,
itemcode, size, (qty - qtyperh), qtyperh
from cte
where qty > 0
)
select cte.*,
dateadd(second, 60 * 60 * target / qtyperh, dt) as end_dt
from cte
order by workorder, dt;
Here is a db<>fiddle.
Is that what are you after?
CREATE TABLE T(
WorkOrder INT,
ItemCode INT,
Size VARCHAR(25),
Qty INT,
QtyPerH INT
);
INSERT INTO T VALUES
(41051, 600111, '14L-16.1', 55, 10),
(41052, 600112, '14L-16.2', 55, 5);
SELECT T.*
FROM T CROSS APPLY
(
SELECT 1 N
FROM master..spt_values
WHERE [Type] = 'P'
AND
[Number] < (T.Qty / T.QtyPerH)
) TT;
Returns:
+-----------+----------+----------+-----+---------+
| WorkOrder | ItemCode | Size | Qty | QtyPerH |
+-----------+----------+----------+-----+---------+
| 41051 | 600111 | 14L-16.1 | 55 | 10 |
| 41051 | 600111 | 14L-16.1 | 55 | 10 |
| 41051 | 600111 | 14L-16.1 | 55 | 10 |
| 41051 | 600111 | 14L-16.1 | 55 | 10 |
| 41051 | 600111 | 14L-16.1 | 55 | 10 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
| 41052 | 600112 | 14L-16.2 | 55 | 5 |
+-----------+----------+----------+-----+---------+
Demo

SQL Server Pagination with different line number per page

I have a table in SQL Server database containing :
int value (column's name : Value)
datetime value (column's name : Date)
bit value (column's name : LastLineOfPage)
I would like to make a pagination query over this table. The logic of the pagination is the following :
The query must return lines corresponding to a given page (parameter #PageNumber), after sorting lines by the Date column
Also, the query must give the SUM of all the previous pages lines
The line number per page is not fixed : by default it's 14 lines per page, but if the bit LastLineOfPage is true, then the page contain only lines until the one with the true value
Here is a synthetic view of the process :
Here is the data in text :
ID DATE VALUE LASTLINEOFPAGE
1 07/10/2006 10 0
2 14/10/2006 12 0
3 21/10/2006 4 1
4 28/10/2006 6 0
5 04/11/2006 8 1
6 25/11/2006 125 0
7 02/12/2006 1 0
8 09/12/2006 5 0
9 16/12/2006 45 0
10 30/12/2006 1 1
So, the query receiving #PageNumber, and also #DefaultLineNumberPerPage (which will be equal to 14 but maybe one day that will change).
Could you help me in the design of this query or SQL function ?
Thanks !
Sample data
I added few rows to illustrate how it works when there are more rows per page than #DefaultLineNumberPerPage. In this example I'll use #DefaultLineNumberPerPage=5 and you'll see how extra pages were generated.
DECLARE #T TABLE (ID int, dt date, VALUE int, LASTLINEOFPAGE bit);
INSERT INTO #T(ID, dt, VALUE, LASTLINEOFPAGE) VALUES
(1 , '2006-10-07', 10 , 0),
(2 , '2006-10-14', 12 , 0),
(3 , '2006-10-21', 4 , 1),
(4 , '2006-10-28', 6 , 0),
(5 , '2006-11-04', 8 , 1),
(6 , '2006-11-25', 125, 0),
(7 , '2006-12-02', 1 , 0),
(8 , '2006-12-09', 5 , 0),
(9 , '2006-12-16', 45 , 0),
(10, '2006-12-30', 1 , 1),
(16, '2007-01-25', 125, 0),
(17, '2007-02-02', 1 , 0),
(18, '2007-02-09', 5 , 0),
(19, '2007-02-16', 45 , 0),
(20, '2007-02-20', 1 , 0),
(26, '2007-02-25', 125, 0),
(27, '2007-03-02', 1 , 0),
(28, '2007-03-09', 5 , 0),
(29, '2007-03-10', 5 , 0),
(30, '2007-03-11', 5 , 0),
(31, '2007-03-12', 5 , 0),
(32, '2007-03-13', 5 , 1),
(41, '2007-10-07', 10 , 0),
(42, '2007-10-14', 12 , 0),
(43, '2007-10-21', 4 , 1);
Query
Run it step-by-step, CTE-by-CTE and examine intermediate results to understand what it does.
CTE_FirstLines sets the FirstLineOfPage flag to 1 for the first line of the page instead of the last.
CTE_SimplePages uses a cumulative SUM to calculate the simple page numbers based on FirstLineOfPage page breaks.
CTE_ExtraPages uses ROW_NUMBER divided by #DefaultLineNumberPerPage to calculate extra page numbers if there is a page that has more than #DefaultLineNumberPerPage rows.
CTE_CompositePages combines simple page numbers with extra page numbers to make a single composite page "Number". It assumes that there will be less than 1000 rows between original LASTLINEOFPAGE flags. If it is possible to have such long sequence of rows, increase the 1000 constant and consider using bigint type for CompositePageNumber column.
CTE_FinalPages uses DENSE_RANK to assign sequential numbers without gaps for each final page.
DECLARE #DefaultLineNumberPerPage int = 5;
DECLARE #PageNumber int = 3;
WITH
CTE_FirstLines
AS
(
SELECT
ID,dt, VALUE, LASTLINEOFPAGE
,CAST(ISNULL(LAG(LASTLINEOFPAGE)
OVER (ORDER BY dt), 1) AS int) AS FirstLineOfPage
FROM #T
)
,CTE_SimplePages
AS
(
SELECT
ID,dt, VALUE, LASTLINEOFPAGE, FirstLineOfPage
,SUM(FirstLineOfPage) OVER(ORDER BY dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS SimplePageNumber
FROM CTE_FirstLines
)
,CTE_ExtraPages
AS
(
SELECT
ID,dt, VALUE, LASTLINEOFPAGE, FirstLineOfPage, SimplePageNumber
,(ROW_NUMBER() OVER(PARTITION BY SimplePageNumber ORDER BY dt) - 1)
/ #DefaultLineNumberPerPage AS ExtraPageNumber
FROM CTE_SimplePages
)
,CTE_CompositePages
AS
(
SELECT
ID,dt, VALUE, LASTLINEOFPAGE, FirstLineOfPage, SimplePageNumber, ExtraPageNumber
,SimplePageNumber * 1000 + ExtraPageNumber AS CompositePageNumber
FROM CTE_ExtraPages
)
,CTE_FinalPages
AS
(
SELECT
ID,dt, VALUE, LASTLINEOFPAGE, FirstLineOfPage, SimplePageNumber, ExtraPageNumber
,CompositePageNumber
,DENSE_RANK() OVER(ORDER BY CompositePageNumber) AS FinalPageNumber
FROM CTE_CompositePages
)
,CTE_Sum
AS
(
SELECT
ID,dt, VALUE, LASTLINEOFPAGE, FirstLineOfPage, SimplePageNumber, ExtraPageNumber
,CompositePageNumber
,FinalPageNumber
,SUM(Value) OVER(ORDER BY FinalPageNumber, dt
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS SumCumulative
FROM CTE_FinalPages
)
SELECT
ID,dt, VALUE, LASTLINEOFPAGE, FirstLineOfPage, SimplePageNumber, ExtraPageNumber
,CompositePageNumber
,FinalPageNumber
,SumCumulative
FROM CTE_Sum
-- WHERE FinalPageNumber = #PageNumber
ORDER BY dt
;
Result with the final WHERE filter commented out
Here is the full result with all intermediate columns to illustrate how the query works.
+----+------------+-------+-----+-----+--------+-------+-----------+-------+------------+
| ID | dt | VALUE | Lst | Fst | Simple | Extra | Composite | Final | TotalValue |
+----+------------+-------+-----+-----+--------+-------+-----------+-------+------------+
| 1 | 2006-10-07 | 10 | 0 | 1 | 1 | 0 | 1000 | 1 | 10 |
| 2 | 2006-10-14 | 12 | 0 | 0 | 1 | 0 | 1000 | 1 | 22 |
| 3 | 2006-10-21 | 4 | 1 | 0 | 1 | 0 | 1000 | 1 | 26 |
| 4 | 2006-10-28 | 6 | 0 | 1 | 2 | 0 | 2000 | 2 | 32 |
| 5 | 2006-11-04 | 8 | 1 | 0 | 2 | 0 | 2000 | 2 | 40 |
| 6 | 2006-11-25 | 125 | 0 | 1 | 3 | 0 | 3000 | 3 | 165 |
| 7 | 2006-12-02 | 1 | 0 | 0 | 3 | 0 | 3000 | 3 | 166 |
| 8 | 2006-12-09 | 5 | 0 | 0 | 3 | 0 | 3000 | 3 | 171 |
| 9 | 2006-12-16 | 45 | 0 | 0 | 3 | 0 | 3000 | 3 | 216 |
| 10 | 2006-12-30 | 1 | 1 | 0 | 3 | 0 | 3000 | 3 | 217 |
| 16 | 2007-01-25 | 125 | 0 | 1 | 4 | 0 | 4000 | 4 | 342 |
| 17 | 2007-02-02 | 1 | 0 | 0 | 4 | 0 | 4000 | 4 | 343 |
| 18 | 2007-02-09 | 5 | 0 | 0 | 4 | 0 | 4000 | 4 | 348 |
| 19 | 2007-02-16 | 45 | 0 | 0 | 4 | 0 | 4000 | 4 | 393 |
| 20 | 2007-02-20 | 1 | 0 | 0 | 4 | 0 | 4000 | 4 | 394 |
| 26 | 2007-02-25 | 125 | 0 | 0 | 4 | 1 | 4001 | 5 | 519 |
| 27 | 2007-03-02 | 1 | 0 | 0 | 4 | 1 | 4001 | 5 | 520 |
| 28 | 2007-03-09 | 5 | 0 | 0 | 4 | 1 | 4001 | 5 | 525 |
| 29 | 2007-03-10 | 5 | 0 | 0 | 4 | 1 | 4001 | 5 | 530 |
| 30 | 2007-03-11 | 5 | 0 | 0 | 4 | 1 | 4001 | 5 | 535 |
| 31 | 2007-03-12 | 5 | 0 | 0 | 4 | 2 | 4002 | 6 | 540 |
| 32 | 2007-03-13 | 5 | 1 | 0 | 4 | 2 | 4002 | 6 | 545 |
| 41 | 2007-10-07 | 10 | 0 | 1 | 5 | 0 | 5000 | 7 | 555 |
| 42 | 2007-10-14 | 12 | 0 | 0 | 5 | 0 | 5000 | 7 | 567 |
| 43 | 2007-10-21 | 4 | 1 | 0 | 5 | 0 | 5000 | 7 | 571 |
+----+------------+-------+-----+-----+--------+-------+-----------+-------+------------+
To get only one given page uncomment the WHERE filter in the final SELECT.
Result with the final WHERE filter
+----+------------+-------+-----+-----+--------+-------+-----------+-------+------------+
| ID | dt | VALUE | Lst | Fst | Simple | Extra | Composite | Final | TotalValue |
+----+------------+-------+-----+-----+--------+-------+-----------+-------+------------+
| 6 | 2006-11-25 | 125 | 0 | 1 | 3 | 0 | 3000 | 3 | 165 |
| 7 | 2006-12-02 | 1 | 0 | 0 | 3 | 0 | 3000 | 3 | 166 |
| 8 | 2006-12-09 | 5 | 0 | 0 | 3 | 0 | 3000 | 3 | 171 |
| 9 | 2006-12-16 | 45 | 0 | 0 | 3 | 0 | 3000 | 3 | 216 |
| 10 | 2006-12-30 | 1 | 1 | 0 | 3 | 0 | 3000 | 3 | 217 |
+----+------------+-------+-----+-----+--------+-------+-----------+-------+------------+
The TotalValue in the last row gives you the total page value that you want to show at the bottom of the page. If you sum all values on this page (125+1+5+45+1 = 177) and subtract it from the last TotalValue (217-177 = 40) you'll get the total of previous pages that you want to show at the top of the page. You'd better do these calculations on the client.
I have a partial solution. Still doesnt count default page size, but can give you an idea. So let me know what you think. Hope you are familiar with CTE's. Test each step so you see what are the partial results.
SQL Demo
WITH cte as (
SELECT [ID], [DATE], [VALUE], [LASTLINEOFPAGE],
SUM([VALUE]) OVER (ORDER BY [ID]) as Total,
SUM([LASTLINEOFPAGE]) OVER (ORDER BY [ID]) as page_group
FROM Table1
),
pages as (
SELECT c1.[ID], c1.[Total],
CASE WHEN c1.[ID] = 1 THEN 0
WHEN c1.[ID] = m.[minID] THEN c1.[page_group] -1
ELSE c1.[page_group]
END as [page_group]
FROM cte as c1
JOIN (SELECT [page_group], MIN([ID]) as minID
FROM cte
GROUP BY [page_group]) m
ON c1.[page_group] = m.[page_group]
)
SELECT c.[ID], c.[DATE], c.[VALUE], c.[LASTLINEOFPAGE],
(SELECT MAX([Total])
FROM pages p2
WHERE p2.[page_group] = p.[page_group]) as [Total],
p.[page_group]
FROM cte c
JOIN pages p
ON c.[ID] = p.[id]
As you can see the total and the page are in the aditional column and you shouldnt display those on your app

Inventory stock calculation query or/and optimization

Remains - all transactions of products
Stock - Stock of products
DimDate - Time table
Calculation of stock:
take the date for which needed calculate stock, example date 20050115 product -1 warehouse - 6
take next month first day example 20050201 and from Stock table take one row with weight, stock, cost for the same product and warehouse.
Sum all transactions in Remains from selected date till month end and subtract from taken row of Stock table
For first day next month I'm using this:
CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime)))-1),DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime))),112)
Example Remains:
| TIMEKEY | PRODUCT | WAREHOUSE | COST | STOCK | WEIGHT |
|----------|---------|-----------|------|-------|--------|
| 20050114 | 1 | 6 | 100 | 5 | 15 |
| 20050118 | 1 | 6 | 200 | 10 | 30 |
| 20050125 | 1 | 6 | -100 | -5 | -15 |
Stock:
| TIMEKEY | PRODUCT | WAREHOUSE | COST | STOCK | WEIGHT |
|----------|---------|-----------|------|-------|--------|
| 20050201 | 1 | 6 | 515 | 55 | 53 |
| 20050301 | 2 | 8 | 20 | 3 | 2 |
| 20050301 | 3 | 9 | 16 | 2 | 3 |
| 20050301 | 4 | 10 | 49 | 8 | 5 |
| 20050401 | 5 | 26 | 59 | 9 | 10 |
Result (my query) calculated stock by day for product 1, warehouse 6 and first month:
| DATEKEY | PRODUCT | WAREHOUSE | WEIGHT | STOCK | COST |
|----------|---------|-----------|--------|--------|--------|
| 20050101 | 1 | 6 | 23 | 45 | 315 |
| 20050102 | 1 | 6 | 23 | 45 | 315 |
| 20050103 | 1 | 6 | 23 | 45 | 315 |
| 20050104 | 1 | 6 | 23 | 45 | 315 |
| 20050105 | 1 | 6 | 23 | 45 | 315 |
| 20050106 | 1 | 6 | 23 | 45 | 315 |
| 20050107 | 1 | 6 | 23 | 45 | 315 |
| 20050108 | 1 | 6 | 23 | 45 | 315 |
| 20050109 | 1 | 6 | 23 | 45 | 315 |
| 20050110 | 1 | 6 | 23 | 45 | 315 |
| 20050111 | 1 | 6 | 23 | 45 | 315 |
| 20050112 | 1 | 6 | 23 | 45 | 315 |
| 20050113 | 1 | 6 | 23 | 45 | 315 |
| 20050114 | 1 | 6 | 38 | 50 | 415 |
| 20050115 | 1 | 6 | 38 | 50 | 415 |
| 20050116 | 1 | 6 | 38 | 50 | 415 |
| 20050117 | 1 | 6 | 38 | 50 | 415 |
| 20050118 | 1 | 6 | 68 | 60 | 615 |
| 20050119 | 1 | 6 | 68 | 60 | 615 |
| 20050120 | 1 | 6 | 68 | 60 | 615 |
| 20050121 | 1 | 6 | 68 | 60 | 615 |
| 20050122 | 1 | 6 | 68 | 60 | 615 |
| 20050123 | 1 | 6 | 68 | 60 | 615 |
| 20050124 | 1 | 6 | 68 | 60 | 615 |
Problem / Question
The stock of product is not ended so I need to show result for full month(or the range I select in where clause), not just till 24 day. IF this immpossible, maybe somebody could help with current query optimization I'm using for calculation stock. I'm Using ms sql 2008r2
SQLFIDDLEExample
SELECT t.DateKey, r.Product, r.Warehouse,
(SELECT [Weight]
FROM Stock
WHERE Timekey = CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime)))-1),DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime))),112)
AND Product = r.Product
AND Warehouse = r.Warehouse)-SUM(r.Weight) AS [Weight],
(SELECT Stock
FROM Stock
WHERE Timekey = CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime)))-1),DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime))),112)
AND Product = r.Product
AND Warehouse = r.Warehouse)-SUM(Stock) as Stock,
(SELECT Cost
FROM Stock
WHERE Timekey = CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime)))-1),DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime))),112)
AND Product = r.Product
AND Warehouse = r.Warehouse)-SUM(Cost) AS Cost
FROM DimDate t, Remains r
WHERE t.DateKey < r.Timekey
AND r.Timekey <= CONVERT(VARCHAR(25),DATEADD(dd,-(DAY(DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime)))-1),DATEADD(mm,1,CAST(convert(varchar,t.DateKey) as datetime))),112)
AND t.DateKey >= 20050101
and r.Product = '1'
and r.Warehouse = 6
GROUP BY t.DateKey, r.Product, r.Warehouse
Hard to tell if it's faster on a small data set, but the following looks to have a much better query plan. The approach is to only join on to the stock table once instead of three times.
with x as (
select
t.DateKey,
r.Product,
r.Warehouse,
sum(r.Weight) as Weight,
sum(Stock) as Stock,
sum(Cost) as Cost,
(t.DateKey / 100
+ Case t.DateKey / 100 % 100 when 12 Then 89 else 1 end)
* 100 + 1 as TimeKey
from
DimDate t,
Remains r
where
t.DateKey < r.Timekey and
r.Timekey <= (
t.DateKey / 100
+ Case t.DateKey / 100 % 100 when 12 Then 89 else 1 end)
* 100 + 1 and
t.DateKey >= 20050101 and
r.Product = 1 and
r.Warehouse = 6
group by
t.DateKey,
r.Product,
r.Warehouse
) select
x.DateKey,
x.Product,
x.Warehouse,
s.Weight - x.Weight as Weight,
s.Stock - x.Stock as Stock,
s.Cost - x.Cost as Cost
From
x
left outer join
Stock s
on s.TimeKey = x.TimeKey and
s.Product = x.Product and
s.Warehouse = x.Warehouse;
Example SQLFiddle
if you have the chance to redesign this schema, bear in mind that the date datatype only uses three bytes, compared to four for an int.
It's probably not contributing much to the overall query cost, but you could store the "start of next month" value as a computed column on the DimDate table. It makes the query a bit easier to read.
create table DimDate (
[DateKey] int,
StartOfNextMonthKey as (
DateKey / 100
+ Case DateKey / 100 % 100 when 12 Then 89 else 1 end
) * 100 + 1 persisted
);
then the query is
with x as (
select
t.DateKey,
r.Product,
r.Warehouse,
sum(r.Weight) AS [Weight],
sum(Stock) as Stock,
sum(Cost) AS Cost,
t.StartOfNextMonthKey
from
DimDate t,
Remains r
where
t.DateKey < r.Timekey AND
r.Timekey <= t.StartOfNextMonthKey and
t.DateKey >= 20050101 and
r.Product = 1 and
r.Warehouse = 6
group by
t.DateKey,
r.Product,
r.Warehouse,
t.StartOfNextMonthKey
) select
x.DateKey,
x.Product,
x.Warehouse,
s.Weight - x.Weight as Weight,
s.Stock - x.Stock as Stock,
s.Cost - x.Cost as Cost
From
x
left outer join
Stock s
on s.TimeKey = x.StartOfNextMonthKey and
s.Product = x.Product and
s.Warehouse = x.Warehouse;
Example SQLFiddle