Sum until value reached zero - sql-server-2008

I have two queries.
The result of the first one is the OnHand quantity of the part in the warehouse:
PartNum OnHandQty IUM
100009 19430.00 KG
The result of the second query are transactions related to the receipts of the goods to the warehouse:
TranDate PartNum TranQty UM Dayss
2014-09-01 100009 10720.000 KG 2
2014-09-01 100009 1340.000 KG 2
2014-08-11 100009 8710.000 KG 23
2014-08-11 100009 3350.000 KG 23
2014-06-30 100009 9380.000 KG 65
Now I need to calculate OnHandQy - TranQty until it hits zero, e.g.
19430 - 10720 = 8710 --not enough
8710 - 1340 = 7370 --not enough
7370 - 8710 = -1340 --enough
As a result I need to receive a table like below:
PartNum OnHandQty IUM [0-10 Days] [11-20 Days] [over 21 Days]
100009 19430.00 KG 12060 null 8710
Any ideas how to get this result?

One idea is to use the following query.
WITH(TranDate,PartNum,TranQty,UM,Dayss) trans
AS
(
-- Your second query
)
SELECT F.PartNum
,F.OnHandQty
,F.IUM
,SUM(t0to10.TransQty) AS [0-10 Days]
,SUM(t11to20.TransQty) AS [11-20 Days]
,SUM(tover20.TransQty) AS [over 21 Days]
FROM (Your first query) AS F
JOIN trans AS t0to10 ON F.PartNum = t0to10.PartNum
AND t0to10.Dayss BETWEEN 0 AND 10
JOIN trans AS t11to20 ON F.PartNum = t11to20.PartNum
AND t11to20.Dayss BETWEEN 11 AND 20
JOIN trans AS tover20 ON F.PartNum = tover20.PartNum
AND tover20.Dayss >20
GROUP BY F.PartNum,F.OnHandQty,F.IUM

Declared the OnHandQty and using cursor in a while loop will help to sort this out.

CREATE TABLE #OnHand(
PartNum NVARCHAR(100)
,OnHandQty DECIMAL
,IUM NVARCHAR(5)
)
INSERT INTO #OnHand
(PartNum, OnHandQty, IUM)
VALUES
('100009', 19430.00, 'KG')
CREATE TABLE #Trans(
TranDate DATETIME
,PartNum NVARCHAR(100)
,TranQty DECIMAL
,IUM NVARCHAR(5))
INSERT INTO #Trans
(TranDate, PartNum, TranQty, IUM)
VALUES
('2014-09-01', '100009', 10720.000, 'KG')
,('2014-09-01', '100009', 1340.000, 'KG')
,('2014-08-11', '100009', 8710.000, 'KG')
,('2014-08-11', '100009', 3350.000, 'KG')
,('2014-06-30', '100009', 9380.000, 'KG')
DECLARE #OnHand_Running DECIMAL
,#OnHandQty DECIMAL
,#TranQty DECIMAL
,#TranDate DATETIME
,#PartNum NVARCHAR(100)
,#First10Days INT = 0
,#Second10Days INT = 0
,#Third10Days INT = 0
SET #PartNum = '100009';
Set #OnHandQty = (Select OnHandQty
FROM #OnHand
WHERE PartNum = #PartNum)
Set #OnHand_Running = #OnHandQty
DECLARE tran_cursor CURSOR
FOR SELECT T.TranQty, T.TranDate
FROM #Trans T
ORDER BY T.TranDate DESC
OPEN tran_cursor
FETCH NEXT FROM tran_cursor
INTO #TranQty, #TranDate
WHILE (##FETCH_STATUS = 0) AND (#OnHandQty > 0)
BEGIN
IF (#OnHandQty - #TranQty) < 0
BEGIN
PRINT CAST(#OnHandQty AS NVARCHAR(100)) + '-' + CAST(#TranQty AS NVARCHAR(100)) + '=' + CAST(#OnHandQty - #TranQty AS NVARCHAR(100)) + ' --Enough'
END
ELSE
BEGIN
PRINT CAST(#OnHandQty AS NVARCHAR(100)) + '-' + CAST(#TranQty AS NVARCHAR(100)) + '=' + CAST(#OnHandQty - #TranQty AS NVARCHAR(100)) + ' --Not Enough'
END
IF (DATEDIFF(DAY, #TranDate, CURRENT_TIMESTAMP) < 10)
SET #First10Days = #First10Days + #TranQty
IF (DATEDIFF(DAY, #TranDate, CURRENT_TIMESTAMP) BETWEEN 10 AND 20)
SET #Second10Days = #Second10Days + #TranQty
IF (DATEDIFF(DAY, #TranDate, CURRENT_TIMESTAMP) > 20)
SET #Third10Days = #Third10Days + #TranQty
SET #OnHandQty = #OnHandQty - #TranQty
FETCH NEXT FROM tran_cursor
INTO #TranQty, #TranDate
END
SELECT #PartNum, #OnHand_Running, 'KG', #First10Days '[0-10] Days', #Second10Days '[11-20] Days', #Third10Days '[over 21 days]'
CLOSE tran_cursor;
DEALLOCATE tran_cursor;
RETURN

Related

Add a digit to the end of an SQL result

I want the result to add number in the end in specific cases.
Like: if result is between 1 and 50, add 1.
If result is between 51 and 99, add 2 to the end.
If result is between 100 and 200, add 3 to the end.
Like:
Result = 25, do it 251.
Result 67, do it 672.
Result is 150, do it 1503.
I have created a table but the cases don't seem to work. How would I add a digit in specific cases?
CREATE TABLE Numbers(
Num INT
);
INSERT Numbers VALUES('12');
INSERT Numbers VALUES('112');
INSERT Numbers VALUES('12');
INSERT Numbers VALUES('122');
INSERT Numbers VALUES('1');
INSERT Numbers VALUES('2');
INSERT Numbers VALUES('12345678');
INSERT Numbers VALUES('12345');
SELECT * FROM Numbers;
SELECT RIGHT('15'+ CONVERT(VARCHAR,Num),6) AS NUM FROM Numbers;
SELECT LEFT(REPLICATE('0', 10) + CONVERT(VARCHAR, Num), 6) AS NUM FROM Numbers;
SELECT RIGHT('0' + CAST(Num AS VARCHAR(2)), 2) FROM Numbers
SELECT
CASE
WHEN Num BETWEEN 1 AND 99
THEN LEFT ('00' + CAST(Num AS VARCHAR(2)), 2)
ELSE
CAST(Num AS VARCHAR(10))
END
FROM Numbers
Since you're already using varchar on these values, I'd use concat - which simply mergs strings together. In this case you simply select what you want to merge, with what. Documentation on Concat() here.
Fiddle: https://www.db-fiddle.com/f/at2fqinuEao3b8coRSydTD/1
SELECT
CASE WHEN Num BETWEEN 1 AND 50
THEN concat(Num, '1')
WHEN Num BETWEEN 51 AND 99
THEN concat(Num, '2')
WHEN Num BETWEEN 100 AND 199
THEN concat(Num, '3')
ELSE Num END AS Num
FROM Numbers
In the examples of your 25,67 and 150 - this is the result:
Num
251
672
1503
You're working with numbers, so you can do Num*10 + 1 etc. Like this. fiddle
SELECT CASE WHEN Num BETWEEN 1 AND 50 THEN Num*10 + 1
WHEN Num BETWEEN 51 AND 99 THEN Num*10 + 2
WHEN Num BETWEEN 100 AND 199 THEN Num*10 + 3
ELSE Num END AS Num
FROM Numbers
That seems like it might be easier than string-casting and concatenating.
But you could do this if you really want strings. fiddle.
SELECT CASE WHEN Num BETWEEN 1 AND 50 THEN CONCAT(Num, '1')
WHEN Num BETWEEN 51 AND 99 THEN CONCAT(Num, '2')
WHEN Num BETWEEN 100 AND 199 THEN CONCAT(Num, '3')
ELSE CONVERT(Num, CHAR) END AS Num
FROM Numbers

SQL Change time values with delayed minutes to "previous" minute

Here's an example:
My database has several entries where the time values are:
10:00:00
10:10:00
10:20:00
...
Then sometimes there are these values where we have a 1 minute delay like:
10:20:00
10:31:00 <-
10:40:00
10:51:00 <-
And I wanted these numbers to, each, go to their respective minutes:
10:20:00
10:31:00 -> 10:30:00
10:40:00
10:51:00 -> 10:50:00
Edit: The time are sole entries, I have my date value in another column, for visualization my columns are
keyDate keyTime Sequency Value
12-03-2018 10:40:00 12 143
Please try this.
SELECT
CONVERT(TIME,CASE WHEN RIGHT(datepart(minute,BeginTime),1) = 1
THEN
CONVERT(VARCHAR(2),datepart(Hour,BeginTime)) +':'
+ CONVERT(VARCHAR(2),datepart(minute,BeginTime) - 1)
+':'
+ CONVERT(VARCHAR(2),datepart(second,BeginTime))
ELSE
CONVERT(VARCHAR(8),BeginTime)
END)
FROM #tbl
For example
Declare #tbl Table(
ArrangeId INT ,
BeginTime TIME,
EndDate NVARCHAR(50),
DeptId INT
)
INSERT INTO #tbl VALUES(1,'7:20:00','22:30',NULL)
INSERT INTO #tbl VALUES(3,'7:10:00','23:00',NULL)
INSERT INTO #tbl VALUES(2,'7:11:00','23:59',NULL)
INSERT INTO #tbl VALUES(4,'6:10:00','22:30',NULL)
INSERT INTO #tbl VALUES(4,'5:11:00','22:30',NULL)
INSERT INTO #tbl VALUES(4,'5:31:00','22:30',NULL)
SELECT
CONVERT(TIME,CASE WHEN RIGHT(datepart(minute,BeginTime),1) = 1
THEN
CONVERT(VARCHAR(2),datepart(Hour,BeginTime)) +':'
+ CONVERT(VARCHAR(2),datepart(minute,BeginTime) - 1)
+':'
+ CONVERT(VARCHAR(2),datepart(second,BeginTime))
ELSE
CONVERT(VARCHAR(8),BeginTime)
END)
FROM #tbl

mysql-function to count days between 2 dates excluding weekends

I've searched through many examples , good ones I got :
Count days between two dates, excluding weekends (MySQL only)
How to count date difference excluding weekend and holidays in MySQL
Calculate diffference between 2 dates in SQL, excluding weekend days
but didn't get most promising solution , so that i can use in my mysql-function for quering lakhs of rows.
This one was very new concept , but didn't worked for inputs like #start_date = '2013-08-03' , #end_date = '2013-08-21' Expected ans : 13 , its giving only 12,
SELECT 5 * (DATEDIFF(#end_date, #start_date) DIV 7) + MID('0123444401233334012222340111123400012345001234550', 7 * WEEKDAY(#start_date) + WEEKDAY(#end_date) + 1, 1);
So i'did tried to make it by myself -
Concept :
Input : 1. period_from_date - from date
2. period_to_date - to date
3. days_to_exclude - mapping : S M T W TH F Sat => 2^0 + 2^6
(sat and sun to exclude) ^ ^ ^ ^ ^ ^ ^
0 1 2 3 4 5 6
DELIMITER $$
USE `db_name`$$
DROP FUNCTION IF EXISTS `FUNC_CALC_TOTAL_WEEKDAYS`$$
CREATE DEFINER=`name`#`%` FUNCTION `FUNC_CALC_TOTAL_WEEKDAYS`( period_from_date DATE, period_to_date DATE, days_to_exclude INT ) RETURNS INT(11)
BEGIN
DECLARE period_total_num_days INT DEFAULT 0;
DECLARE period_total_working_days INT DEFAULT 0;
DECLARE period_extra_days INT DEFAULT 0;
DECLARE period_complete_weeks INT DEFAULT 0;
DECLARE extra_days_start_date DATE DEFAULT '0000-00-00';
DECLARE num_days_to_exclude INT DEFAULT 0;
DECLARE start_counter_frm INT DEFAULT 0;
DECLARE end_counter_to INT DEFAULT 6;
DECLARE temp_var INT DEFAULT 0;
# if no day to exclude return date-diff only
IF days_to_exclude = 0 THEN
RETURN DATEDIFF( period_to_date, period_from_date ) + 1 ;
END IF;
# get total no of days to exclude
WHILE start_counter_frm <= end_counter_to DO
SET temp_var = POW(2,start_counter_frm) ;
IF (temp_var & days_to_exclude) = temp_var THEN
SET num_days_to_exclude = num_days_to_exclude + 1;
END IF;
SET start_counter_frm = start_counter_frm + 1;
END WHILE;
# Get period days count
SET period_total_num_days = DATEDIFF( period_to_date, period_from_date ) + 1 ;
SET period_complete_weeks = FLOOR( period_total_num_days /7 );
SET period_extra_days = period_total_num_days - ( period_complete_weeks * 7 );
SET period_total_working_days = period_complete_weeks * (7 - num_days_to_exclude);
SET extra_days_start_date = DATE_SUB(period_to_date,INTERVAL period_extra_days DAY);
# get total working days from the left days
WHILE period_extra_days > 0 DO
SET temp_var = DAYOFWEEK(period_to_date) -1;
IF POW(2,temp_var) & days_to_exclude != POW(2,temp_var) THEN
SET period_total_working_days = period_total_working_days +1;
END IF;
SET period_to_date = DATE_SUB(period_to_date,INTERVAL 1 DAY);
SET period_extra_days = period_extra_days -1;
END WHILE;
RETURN period_total_working_days;
END$$
DELIMITER ;
Please let me know the holes where this would fail.Open to any suggestions and comments.
UPDATED: If you just need a number of weekdays between two dates you can get it like this
CREATE FUNCTION TOTAL_WEEKDAYS(date1 DATE, date2 DATE)
RETURNS INT
RETURN ABS(DATEDIFF(date2, date1)) + 1
- ABS(DATEDIFF(ADDDATE(date2, INTERVAL 1 - DAYOFWEEK(date2) DAY),
ADDDATE(date1, INTERVAL 1 - DAYOFWEEK(date1) DAY))) / 7 * 2
- (DAYOFWEEK(IF(date1 < date2, date1, date2)) = 1)
- (DAYOFWEEK(IF(date1 > date2, date1, date2)) = 7);
Note: The function will still work if you switch start date1 and end date2 dates.
Sample usage:
SELECT TOTAL_WEEKDAYS('2013-08-03', '2013-08-21') weekdays1,
TOTAL_WEEKDAYS('2013-08-21', '2013-08-03') weekdays2;
Output:
| WEEKDAYS1 | WEEKDAYS2 |
-------------------------
| 13 | 13 |
Here is DBFiddle demo
This query will work fine, all the queries above are not working well. Try this :
SELECT ((DATEDIFF(date2, date1)) -
((WEEK(date2) - WEEK(date1)) * 2) -
(case when weekday(date2) = 6 then 1 else 0 end) -
(case when weekday(date1) = 5 then 1 else 0 end)) as DifD
Test it like this :
SELECT ((DATEDIFF('2014-10-25', '2014-10-15')) -
((WEEK('2014-10-25') - WEEK('2014-10-15')) * 2) -
(case when weekday('2014-10-25') = 6 then 1 else 0 end) -
(case when weekday('2014-10-15') = 5 then 1 else 0 end)) as DifD
The result :
DifD
8
I use this. Means there are no functions so can be used in views:
select
datediff(#dateto, #datefrom) +
datediff(#datefrom,
date_add(#datefrom, INTERVAL
floor(datediff(#dateto, #datefrom) / 7) day)) * 2
- case
when weekday(#dateto) = 6 then 2
when weekday(#dateto) = 5 then 1
when weekday(#dateto) < weekday(#datefrom) then 2
else 0
end;
Had a similar issue, I used PHP to remove the weekends, need to know start day and number of days:
EG SQL:
SELECT DAYOFWEEK(`date1`) AS `startday`, TIMESTAMPDIFF(DAY, `date1`, `date2`) AS `interval` FROM `table`
Then run the result through a PHP function:
function noweekends($startday, $interval) {
//Remove weekends from an interval
$wecount = 0; $tmp = $interval;
while($interval/7 > 1) { $interval-=7; $wecount++; }
if($interval+$startday > 5) $wecount++;
$interval = $tmp-($wecount*2);
return $interval;
}
To exclude only Sunday:
CREATE FUNCTION TOTAL_WEEKDAYS(date1 DATE, date2 DATE)
RETURNS INT
RETURN ABS(DATEDIFF(date2, date1)) + 1
- ABS(DATEDIFF(ADDDATE(date2, INTERVAL 1 - DAYOFWEEK(date2) DAY),
ADDDATE(date1, INTERVAL 1 - DAYOFWEEK(date1) DAY))) / 7
- (DAYOFWEEK(IF(date1 < date2, date1, date2)) = 1);
You can also create triggers to automatically calculate it in another column, and you can specify legal holidays in another table:
CREATE OR REPLACE TRIGGER `vacation_before_insert` BEFORE INSERT ON `vacation` FOR EACH ROW
BEGIN
SET #start_date = NEW.Start_date;
SET #end_date = NEW.End_date;
SET #numofholydays = (IFNULL((SELECT SUM(IF(`Date` BETWEEN NEW.Start_date AND NEW.End_date, 1, 0)) as numofdays FROM free_legal_days),0));
SET #totaldays = DATEDIFF(#end_date , #start_date) + 1;
SET #saturdays = WEEK(DATE_ADD(#end_date, INTERVAL 1 DAY))-WEEK(#start_date);
SET #sundays = WEEK(#end_date) - WEEK(#start_date);
SET NEW.Number_of_days = #totaldays-#saturdays-#sundays-#numofholydays;
END;
CREATE OR REPLACE TRIGGER `vacation_before_update` BEFORE UPDATE ON `vacation` FOR EACH ROW
BEGIN
SET #start_date = NEW.Start_date;
SET #end_date = NEW.End_date;
SET #numofholydays = (IFNULL((SELECT SUM(IF(`Date` BETWEEN NEW.Start_date AND NEW.End_date, 1, 0)) as numofdays FROM free_legal_days),0));
SET #totaldays = DATEDIFF(#end_date , #start_date) + 1;
SET #saturdays = WEEK(DATE_ADD(#end_date, INTERVAL 1 DAY))-WEEK(#start_date);
SET #sundays = WEEK(#end_date) - WEEK(#start_date);
SET NEW.Number_of_days = #totaldays-#saturdays-#sundays-#numofholydays;
END;

Add 28 to last 2 digit of date and replace the order

I have a number such as this : 840106
I need to do the following :
Change the number to date add - and flip the number : 06-01-84
add 28 to the last 2 digit that the date will be : 06-01-12
84 + 16 = 00 + 12 = 12
number is always changing sometimes it cab be 850617 , but format is always same add - and add 28 last 2 digit.
any ideas how to help me here ?
Here is a sqlite solution:
create table t( c text);
insert into t (c) values(990831);
insert into t (c) values(840106);
insert into t (c) values(800315);
insert into t (c) values(750527);
insert into t (c) values(700923);
insert into t (c) values(620308);
select c, substr(c,5,2) || '-' || substr(c,3,2) || '-' ||
case when (substr(c,1,2) + 28) < 100 then (substr(c,1,2) + 28)
else case when ((substr(c,1,2) + 28) - 100) < 10 then '0' || ((substr(c,1,2) + 28) - 100)
else ((substr(c,1,2) + 28) - 100)
end
end
from t;
For formatting you can use
http://www.w3schools.com/sql/func_date_format.asp
For adding days to the date you should take a look at date_add() function
mysql> SELECT DATE_ADD('1998-01-02', INTERVAL 31 DAY);
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-add
Assuming date is the name of the column containing your date:
DATE_FORMAT(DATE_ADD(STR_TO_DATE(date, %y%m%d), INTERVAL 28 YEAR), %d-%m-%y);
What this does is first formats the string into a date, then adds 28 years, then converts back to string with the new format.
SQLite is a lot tricker with this, you'll need to use substrings.
substr(date,5) || "-" || substr(date,3,4) || "-" || CAST(CAST(substr(date,1,2) as integer) + 28 - 100) as text
I'm not too experienced with SQLite so the casting may be a bit weird.
Here is a t-sql solution that you can use and migrate to mysql.
declare #myDate as char(8) = '840106';
declare #y as char(2), #m as char(2), #d as char(2);
set #y = LEFT (#myDate, 2);
declare #yi as int = Convert (int, #y);
IF #y between 30 and 99 ----------- pick a cut-off year
SET #yi = (28 - (100-#yi));
SET #y = CONVERT(char, #yi)
set #m = SUBSTRING(#myDate, 3, 2);
set #d = SUBSTRING(#myDate, 5, 2);
SET #myDate = #d + '-' + #m + '-' + #y;
PRINT #myDate;

How to round a SQL time variable?

I have declared a time variable with value 23:59:59. So, I need to round it to 24:00. Have you any idea?
declare #t1 time = '23:59:59'
This is necessary only in the select statement. I know that time cannot be inserted as 24:00.
http://msdn.microsoft.com/en-us/library/bb677243(v=sql.105).aspx
How about:
--setup
declare #t1 time = '23:59:59'
--declare #t1 time = '00:59:59'
--declare #t1 time = '00:59:29'
--declare #t1 time = '00:00:00'
--solution for rounding to nearest hour
declare #temp1 int = datepart(minute, #t1) / 30
set #temp1 = (DATEPART(hour, #t1) % 24) + #temp1
select #t1, cast(#temp1 as nvarchar(2)) + ':00' --not too worried about rpading hours since 1:00 makes sense
--solution for rounding to nearest minute
declare #ss int = datepart(second, #t1)/30
, #mm int = datepart(minute, #t1)
, #hh int = datepart(hour, #t1)
, #oo nchar(2) = N'00' --used for padding
set #hh = (#hh + (#mm / 30) * #ss) % 24
set #mm = (#mm + #ss) % 60
select #t1
, substring(#oo + cast(#hh as nvarchar(2)), case when #hh > 9 then 3 when #hh = 0 then 1 else 2 end, 2) --hours rpaded
+ N':'
+ substring(#oo + cast(#mm as nvarchar(2)), case when #mm > 9 then 3 when #mm = 0 then 1 else 2 end, 2) --minutes rpaded