How to parse SQL Server String in hour, min and second - sql

I have time stored as PT1H22M59.551S.
There it reads read 1 hour 22 min and 59.551 sec.
What is the most efficient way to split this?
Data could be like this. So no separator is mandatory
PT0S
PT6H4M29.212S
PT0S
PT2M55.126S
PT54M4.12S
PT3H6M5.74S
PT16H27M52.069S

You can also create a function to perform the conversion with basic text manipulation:
CREATE FUNCTION dbo.f_GetTime(#TimeInput VARCHAR(50)) RETURNS TIME
AS BEGIN
SET #TimeInput = REPLACE(REPLACE(#TimeInput, 'PT', ''), 'S', '')
IF CHARINDEX('H', #TimeInput) = 0 -- Add missing H
SET #TimeInput = '0H' + #TimeInput
IF CHARINDEX('M', #TimeInput) = 0 -- Add missing M
SET #TimeInput = REPLACE(#TimeInput, 'H', 'H0M')
RETURN CONVERT(TIME, REPLACE(REPLACE(#TimeInput, 'H', ':'), 'M', ':'))
END
GO
Then you can use it with just scalar queries:
SELECT dbo.f_GetTime('PT1H22M59.551S') AS TimeValue
Or you can use it with tables:
DECLARE #Input TABLE (TimeInput VARCHAR(20))
INSERT #Input VALUES ('PT1H22M59.551S'), ('PT0S'), ('PT6H4M29.212S'), ('PT0S'),
('PT2M55.126S'), ('PT54M4.12S'), ('PT3H6M5.74S'), ('PT16H27M52.069S')
SELECT TimeInput, dbo.f_GetTime(TimeInput) AS TimeValue FROM #Input
This would return:
TimeInput TimeValue
-------------------- ----------------
PT1H22M59.551S 01:22:59.5510000
PT0S 00:00:00.0000000
PT6H4M29.212S 06:04:29.2120000
PT0S 00:00:00.0000000
PT2M55.126S 00:02:55.1260000
PT54M4.12S 00:54:04.1200000
PT3H6M5.74S 03:06:05.7400000
PT16H27M52.069S 16:27:52.0690000

--sql server
declare #data table(
lineid int identity,
a varchar(100),
b varchar(100),
hr varchar(20),
[minute] varchar(20),
[second] varchar(20)
)
insert #data(a) select 'PT0S'
insert #data(a) select 'PT6H4M29.212S'
insert #data(a) select 'PT0S'
insert #data(a) select 'PT2M55.126S'
insert #data(a) select 'PT54M4.12S'
insert #data(a) select 'PT3H6M5.74S'
insert #data(a) select 'PT16H27M52.069S'
update #data
set b = replace(a,'PT','')
update #data
set hr = LEFT(b,charindex('H',b))
update #data
set b = REPLACE(b,hr,'')
update #data
set [minute] = LEFT(b,charindex('M',b))
update #data
set b = REPLACE(b,[minute],'')
update #data
set [second] = LEFT(b,charindex('S',b))
update #data
set b = REPLACE(b,[second],'')
update #data
set [hr] = REPLACE([hr],'H',''),[minute]=REPLACE([minute],'M',''),[second]=REPLACE([second],'S','')
update #data
set hr = isnull(nullif(hr,''),'0'),[minute] = isnull(nullif([minute],''),'0'),[second] = isnull(nullif([second],''),'0')
select *,
[hr] + ' hour'+ case when hr <> '1' then 's ' else ' ' end +
[minute] + ' minute'+ case when [minute] <> '1' then 's ' else ' ' end +
[second] + ' second(s)'
from #data

--sql server
declare #data table(
lineid int identity,
[inboundTime] varchar(100)
)
insert #data([inboundTime]) select 'PT0S'
insert #data([inboundTime]) select 'PT6H4M29.212S'
insert #data([inboundTime]) select 'PT0S'
insert #data([inboundTime]) select 'PT2M55.126S'
insert #data([inboundTime]) select 'PT54M4.12S'
insert #data([inboundTime]) select 'PT3H6M5.74S'
insert #data([inboundTime]) select 'PT16H27M52.069S'
insert #data([inboundTime]) select 'PT52.069S'
update #data
set [inboundTime] = replace([inboundTime],'PT','')
SELECT
INBOUND_H
, INBOUND_M
, INBOUND_S
, INBOUND_H*3600 + INBOUND_M*60 inboundTimeMin
, [inboundTime]
from (
SELECT
IIF ( CHARINDEX('H', [inboundTime]) > 0 , SUBSTRING([inboundTime], 0, CHARINDEX('H', [inboundTime])),0) INBOUND_H
, IIF ( CHARINDEX('M', [inboundTime]) > 0 , SUBSTRING([inboundTime], CHARINDEX('H', [inboundTime])+1,CHARINDEX('M', [inboundTime]) - CHARINDEX('H', [inboundTime])-1 ), 0) INBOUND_M
, SUBSTRING([inboundTime], CHARINDEX('M', [inboundTime])+1,CHARINDEX('S', [inboundTime]) - CHARINDEX('M', [inboundTime])-1 ) INBOUND_S
, [inboundTime]
FROM #data)x

This is a more robust function built off solution:
CREATE FUNCTION dbo.f_GetTime(#TimeInput VARCHAR(50)) RETURNS VARCHAR(50)
AS BEGIN
SET #TimeInput = REPLACE(REPLACE(#TimeInput, 'PT', ''), 'S', '')
IF CHARINDEX('H', REVERSE(#TimeInput)) = 1
SET #TimeInput = #TimeInput + '00:00' -- Add missing S
IF CHARINDEX('M', REVERSE(#TimeInput)) = 1
SET #TimeInput = #TimeInput + '00' -- Add missing S
IF CHARINDEX('H', #TimeInput) = 0 -- Add missing H
SET #TimeInput = '00H' + #TimeInput
IF CHARINDEX('M', #TimeInput) = 0 -- Add missing M
SET #TimeInput = REPLACE(#TimeInput, 'H', 'H0M')
RETURN CONVERT(VARCHAR(50), REPLACE(REPLACE(#TimeInput, 'H', ':'), 'M', ':'))
END
GO

Related

SQL Function - Fuzzy Matching with Levenshtein Distance Algorithm - Return Lowest Value Only

Problem: Need SQL function to return the 'lowest' matching value using the Levenshtein algorithm.
Code:
CREATE FUNCTION ufn_levenshtein(#s1 nvarchar(3999), #s2 nvarchar(3999))
RETURNS int
AS
BEGIN
DECLARE #s1_len int, #s2_len int
DECLARE #i int, #j int, #s1_char nchar, #c int, #c_temp int
DECLARE #cv0 varbinary(8000), #cv1 varbinary(8000)
SELECT
#s1_len = LEN(#s1),
#s2_len = LEN(#s2),
#cv1 = 0x0000,
#j = 1, #i = 1, #c = 0
WHILE #j <= #s2_len
SELECT #cv1 = #cv1 + CAST(#j AS binary(2)), #j = #j + 1
WHILE #i <= #s1_len
BEGIN
SELECT
#s1_char = SUBSTRING(#s1, #i, 1),
#c = #i,
#cv0 = CAST(#i AS binary(2)),
#j = 1
WHILE #j <= #s2_len
BEGIN
SET #c = #c + 1
SET #c_temp = CAST(SUBSTRING(#cv1, #j+#j-1, 2) AS int) +
CASE WHEN #s1_char = SUBSTRING(#s2, #j, 1) THEN 0 ELSE 1 END
IF #c > #c_temp SET #c = #c_temp
SET #c_temp = CAST(SUBSTRING(#cv1, #j+#j+1, 2) AS int)+1
IF #c > #c_temp SET #c = #c_temp
SELECT #cv0 = #cv0 + CAST(#c AS binary(2)), #j = #j + 1
END
SELECT #cv1 = #cv0, #i = #i + 1
END
RETURN #c
END
IF OBJECT_ID('tempdb..#ExistingCustomers') IS NOT NULL
DROP TABLE #ExistingCustomers;
CREATE TABLE #ExistingCustomers
(
Customer VARCHAR(255),
ID INT
)
INSERT #ExistingCustomers SELECT 'Ed''s Barbershop', 1002
INSERT #ExistingCustomers SELECT 'GroceryTown', 1003
INSERT #ExistingCustomers SELECT 'Candy Place', 1004
INSERT #ExistingCustomers SELECT 'Handy Man', 1005
IF OBJECT_ID('tempdb..#POTENTIALCUSTOMERS') IS NOT NULL
DROP TABLE #POTENTIALCUSTOMERS;
CREATE TABLE #POTENTIALCUSTOMERS(Customer VARCHAR(255));
INSERT #POTENTIALCUSTOMERS SELECT 'Eds Barbershop'
INSERT #POTENTIALCUSTOMERS SELECT 'Grocery Town'
INSERT #POTENTIALCUSTOMERS SELECT 'Candy Place'
INSERT #POTENTIALCUSTOMERS SELECT 'Handee Man'
INSERT #POTENTIALCUSTOMERS SELECT 'The Apple Farm'
INSERT #POTENTIALCUSTOMERS SELECT 'Ride-a-Long Bikes'
SELECT A.Customer,
b.ID,
b.Customer as cust,
dbo.ufn_levenshtein(REPLACE(A.Customer, ' ', ''), REPLACE(B.Customer, ' ', '')) as ValueLev
FROM #POTENTIALCUSTOMERS a
LEFT JOIN #ExistingCustomers b ON dbo.ufn_levenshtein(REPLACE(A.Customer, ' ', ''), REPLACE(B.Customer, ' ', '')) < 15;
This returns:
What I would like to return:
Explanation: The results are the 'lowest' values from the Levenshtein algorithm. There are two rows where the Levenshtein scores are the same The Apple Farm and Ride-a-Long Bikes, in which case any of the values is fine, just as long as it is one value.
References:
SQL Fuzzy Join - MSSQL
http://www.kodyaz.com/articles/fuzzy-string-matching-using-levenshtein-distance-sql-server.aspx
You can use CTE to get the result you want if you partition by the potential customer and use the ValueLev to order the results:
;WITH CTE AS
(
SELECT RANK() OVER (PARTITION BY a.Customer ORDER BY dbo.ufn_levenshtein(REPLACE(A.Customer, ' ', ''), REPLACE(B.Customer, ' ', '')) ASC) AS RowNbr,
A.Customer,
b.ID,
b.Customer as cust,
dbo.ufn_levenshtein(REPLACE(A.Customer, ' ', ''), REPLACE(B.Customer, ' ', '')) as ValueLev
FROM #POTENTIALCUSTOMERS a
LEFT JOIN #ExistingCustomers b ON dbo.ufn_levenshtein(REPLACE(A.Customer, ' ', ''), REPLACE(B.Customer, ' ', '')) < 15
)
SELECT Customer,
MIN(ID) AS ID,
MIN(cust) AS cust,
ValueLev
FROM CTE
WHERE CTE.RowNbr = 1
GROUP BY Customer, ValueLev
As you don't mind which result is returned in the case of duplicate ValueLev, use GROUP BY and MIN to scale the results down to one per potential customer.
Output:
Customer ID cust ValueLev
Candy Place 1004 Candy Place 0
Grocery Town 1003 GroceryTown 0
Eds Barbershop 1002 Ed's Barbershop 1
Handee Man 1005 Handy Man 2
The Apple Farm 1004 Candy Place 9
Ride-a-Long Bikes 1003 Candy Place 14

Converting CHAR string to nth letter in Alphabet string in SQL

I have to build a process that takes a VARCHAR string (for example 'AHT559') and converts it to a INT only string by converting the Alphabetic chars to INTEGERS based on the nth letter in the alphabet. The above would thus result in: 010820559.
I have done this in SAS before, but I'm relatively new to SQL. What would be the best way to do this in SQL?
Here is what I've done in SAS:
DO _i = 1 TO length( account );
IF (rank( char( account, _i ) ) -64) < 0 THEN agreement_hash = CATS( agreement_hash, char( account, _i ) );
ELSE IF (rank( char( account, _i ) ) -64) < 10 THEN agreement_hash = CATS( agreement_hash, 0, rank( char( account, _i ) )-64 );
ELSE agreement_hash = CATS( agreement_hash, rank( char( account, _i ) )-64 );
END;
If the format of the values is always the same as you state in the comments and you only need to process a single value at a time you can do some simple string manipulation to convert the characters to integers using their ASCII values, and subtracting 64 to get the number of the alphabetic character:
SELECT ASCII('A') -- produces 65
SELECT ASCII('A') - 64 -- produces 1
This is a little long winded and could be done in less lines of code, but it's separated for clarity.
DECLARE #val NVARCHAR(10) = 'AHT559'
-- get first, second and third character numeric values
DECLARE #first INT = ASCII(SUBSTRING(#val, 1, 1)) - 64
DECLARE #second INT = ASCII(SUBSTRING(#val, 2, 1)) - 64
DECLARE #third INT = ASCII(SUBSTRING(#val, 3, 1)) - 64
-- join them together adding a '0' if < 10
SELECT RIGHT('0' + CAST(#first AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST(#second AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST(#third AS VARCHAR(2)), 2)
+ RIGHT(#val, 3)
Tested on 4 million rows:
-- temp table creation - takes approx 100 seconds on my machine
CREATE TABLE #temp (val NVARCHAR(6))
DECLARE #rowno INT = 1
SELECT #rowno = 1
WHILE #rowno <= 4000000
BEGIN
INSERT INTO #temp ( val ) VALUES ( 'AHT559' )
SELECT #rowno = #rowno + 1
END
To run this code against the entire temp table takes < 20 seconds on my machine:
SELECT val AS OrignalValue,
RIGHT('0' + CAST( ASCII(SUBSTRING(val, 1, 1)) - 64 AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST( ASCII(SUBSTRING(val, 2, 1)) - 64 AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST( ASCII(SUBSTRING(val, 3, 1)) - 64 AS VARCHAR(2)), 2)
+ RIGHT(val, 3) AS FormattedValue
FROM #temp
Here is a similar script for sqlserver, any character which is not a capital letter is assumed a digit in this syntax:
DECLARE #x varchar(100) = 'AHT559'
DECLARE #p int = len(#x)
WHILE #p > 0
SELECT #x =
CASE WHEN substring(#x, #p, 1) between 'A' and 'Z'
THEN stuff(#x, #p, 1, right(ascii(substring(#x, #p, 1)) - 64 + 100, 2))
ELSE #x END,
#p -= 1
SELECT #x
Result:
010820559
You could use something like the below, possibly as a scalar function to do this conversion.
DECLARE #i INT
DECLARE #Item NVARCHAR(4000) = 'AHT1234'
DECLARE #ItemTable TABLE
(
Item NCHAR(1)
)
SET #i = 1
--Split the input string into separate characters, store in temp table
WHILE (#i <= LEN(#Item))
BEGIN
INSERT INTO #ItemTable(Item)
VALUES(SUBSTRING(#Item, #i, 1))
SET #i = #i + 1
END
DECLARE #AlphaTable TABLE (
Letter NCHAR(1),
Position NVARCHAR(2)
)
-- Populate this with the whole alphabet obviously. Could be a permanent rather than temp table.
INSERT INTO #AlphaTable
( Letter, Position )
VALUES ( N'A', '01'),
(N'H', '08'),
(N'T', '20')
DECLARE #Output NVARCHAR(50)
-- Convert the output and concatenate it back to a single output.
SELECT #Output = COALESCE(#output, '') + Converted
FROM (
SELECT CASE WHEN ISNUMERIC(Item) = 1
THEN CONVERT(NVARCHAR(1), Item)
ELSE (SELECT Position FROM #AlphaTable WHERE Letter = CONVERT(NCHAR(1), Item))
END AS Converted
FROM #ItemTable
) AS T1
SELECT #Output
GO
Try this.
DECLARE #STR VARCHAR(MAX)= 'AHT559',
#SP INT,
#SP_STR VARCHAR(50),
#OUTPUT VARCHAR(MAX)=''
DECLARE #TEMP_STR VARCHAR(50)
SET #TEMP_STR = #STR
WHILE Patindex('%[A-Z]%', #TEMP_STR) <> 0
BEGIN
SELECT #SP = Patindex('%[A-Z]%', #TEMP_STR)
SELECT #SP_STR = Upper(LEFT(#TEMP_STR, #SP))
SELECT #SP_STR = ( Ascii(#SP_STR) - 65 ) + 1
SELECT #TEMP_STR = Stuff(#TEMP_STR, 1, #SP, '')
SET #OUTPUT += RIGHT('0' + #SP_STR, 2)
END
SELECT #OUTPUT + Substring(#STR, Patindex('%[0-9]%', #STR), Len(#STR))
How about using a CTE to create every combination of the first 3 letters and using that to match to:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Accounts
(
Account VARCHAR(6)
)
INSERT INTO Accounts
VALUES ('AHT559'), ('BXC556'),
('CST345')
Query 1:
;WITH AlphaToNum
AS
(
SELECT *
FROM (VALUES
('A', '01'), ('B', '02'), ('C', '03'), ('D', '04'),
('E', '05'), ('F', '06'), ('G', '07'), ('H', '08'),
('I', '09'), ('J', '10'), ('K', '11'), ('L', '12'),
('M', '13'), ('N', '14'), ('O', '15'), ('P', '16'),
('Q', '17'), ('R', '18'), ('S', '19'), ('T', '20'),
('U', '21'), ('V', '22'), ('W', '23'), ('X', '24'),
('Y', '25'), ('Z', '26')
) X(alpha, num)
),
MappingTable
As
(
SELECT A1.alpha + A2.alpha + A3.alpha as match, A1.num + A2.num + A3.num as val
FROM AlphaToNum A1
CROSS APPLY AlphaToNum A2
CROSS APPLY AlphaToNum A3
)
SELECT A.Account, M.val + SUBSTRING(A.Account,4, 3) As ConvertedAccount
FROM MappingTable M
INNER JOIN Accounts A
ON LEFT(A.Account,3) = M.match
Results:
| Account | ConvertedAccount |
|---------|------------------|
| AHT559 | 010820559 |
| BXC556 | 022403556 |
| CST345 | 031920345 |
This is probably best done using a CLR UDF, but a full answer is too long for this format.
Basically you need to create a UDF (User defined function) that takes a string (nvarchar...) as an input and returns a string as an output. You can do that with C# quite easily, and you need to wrap it with the CLR integration requirements.
You can see here for relevant information.
The code could look something like:
[Microsoft.SqlServer.Server.SqlFunction(
IsDeterministic=true,
IsPrecise=true,
SystemDataAccess=SystemDataAccessKind.None)]
public static SqlString ToNthAlpha(SqlString value)
{
if(value.IsNull)
return value;
char []chars = value.Value.ToCharArray();
StringBuilder res = new StringBuilder();
for(int i = 0; i < chars.Length; i++)
{
if(chars[i] >= 'A' && chars[i] <= 'Z')
res.AppendFormat("{0:00}", chars[i] - 'A');
res.Append(chars[i]);
}
return new SqlString(res.ToString());
}

Stored Procedure to Insert comma seperated values as multiple records

Please help me in creating a stored procedure which accepts comma separated values and inserts as multiple rows.
So one parameter #Name will contain values A,B,C and the other parameter #Id will contain values as 1,2,3
The table values after insertion should be as below:
Name Id
------------
A 1
A 2
A 3
B 1
B 2
B 3
C 1
C 2
C 3
How can I write a stored procedure that can insert the comma-separated values as shown above. Also, If the table already consists of a Name,id pair for example, if A,2 is already there in the table, then it should not insert.
I am using SQL Server 2005. Thanks in advance.
Something like this?
DECLARE #var1 VARCHAR(100)='A,B,C';
DECLARE #var2 VARCHAR(100)='1,2,3';
WITH rep1(name, delim) AS
(
SELECT #var1 name, ',' delim
UNION ALL
SELECT LEFT(name, CHARINDEX(delim, name, 1) - 1) name, delim
FROM rep1
WHERE (CHARINDEX(delim, name, 1) > 0)
UNION ALL
SELECT RIGHT(name, LEN(name) - CHARINDEX(delim, name, 1)) name, delim
FROM rep1
WHERE (CHARINDEX(delim, name, 1) > 0)
)
,rep2(id, delim) AS
(
SELECT #var2 id, ',' delim
UNION ALL
SELECT LEFT(id, CHARINDEX(delim, id, 1) - 1) id, delim
FROM rep2
WHERE (CHARINDEX(delim, id, 1) > 0)
UNION ALL
SELECT RIGHT(id, LEN(id) - CHARINDEX(delim, id, 1)) id, delim
FROM rep2
WHERE (CHARINDEX(delim, id, 1) > 0)
)
INSERT #table
(Name
,ID)
SELECT
r1.name
,r2.id
FROM rep1 r1
CROSS JOIN rep2 r2
LEFT JOIN #table t
ON r2.id=t.id
AND t.name=r1.name
WHERE (CHARINDEX(r1.delim, r1.name, 1) = 0)
AND (CHARINDEX(r2.delim, r2.id, 1) = 0)
AND t.name IS NULL
ORDER BY r1.name
,r2.id
OPTION (MAXRECURSION 0);
Here we are sepearting Comma Seperated into rows
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
IF OBJECT_ID('tempdb..#NewTemp') IS NOT NULL
DROP TABLE #NewTemp
Declare #Testdata table ( name Varchar(max), Data varchar(max))
insert #Testdata select 'A', '1,2,3'
insert #Testdata select 'B', '1,2,3'
insert #Testdata select 'C', '1,2'
insert #Testdata select 'A', '1,2,3,4'
insert #Testdata select 'C', '1,2,3,4,5'
;with tmp(name, DataItem, Data) as (
select name, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from #Testdata
union all
select name, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
from tmp
where Data > ''
)
Then Inserting into Temp Table
select DISTINCT name, DataItem INTO #Temp
from tmp WHERE EXISTS (Select DISTINCT name,DataItem from tmp)
order by name
Here we are controlling entry of Duplicates we can observe combination won't repeat like (A,1),(B,1)Even though they are multiple
CREATE TABLE #NewTemp(name Varchar(max), Data varchar(max))
INSERT INTO #NewTemp (name,Data)
Select name,DataItem from #Temp
Select * FROM #NewTemp
You can go and create one user defined functions for splitting the comma separated values into rows as below
How this function will work and more on it can be found here
CREATE FUNCTION dbo.Split
(
#RowData nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Data nvarchar(100)
)
AS
BEGIN
Declare #Cnt int
Set #Cnt = 1
DECLARE #index INT
SET #index = Charindex(#SplitOn,#RowData)
While (#index>0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,#index-1)))
Set #RowData = Substring(#RowData,#index+1,len(#RowData))
Set #Cnt = #Cnt + 1
SET #index = Charindex(#SplitOn,#RowData)
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END
Once this function is created, you can use it for your requirement as below
declare #Name VARCHAR(30)
declare #Id VARCHAR(30)
SET #Name = 'A,B,C'
SET #Id = '1,2,3'
select A.Data,B.Data FROM dbo.Split(#name,',') A ,dbo.Split(#id,',') B

Update data based on parameter with operator from another table

I have 2 tables with sample data below:
tblSale
PartCode PartGroup SaleQty
a FM 600
b MM 202
c SM 10
d NM 0
tblCondition
PartGroup Condition
FM >500
MM >=200
SM >=1
NM 0
in SQL Server stored procedure i want to update PartGroup in tblSale by PartGroup in tblCondition based on sum(SaleQty) and compare with Condition.
Any help please.
UPDATE:
Example:
PartCode 'A' is has PartGroup='FM' and SaleQty=500.
if SaleQty=400 then update PartGroup='MM' based on Condition in tblCondition.
UPDATE tblSale
SET tblSale.PartGroup=tblCondition.PartGroup
WHERE SUM(tblSale.Sale) ??? tblCondition.Condition
I don't think you will be able to do this without dynamic code.
For my solution you will need make some changes/notes:
change PartGroup NM condition 0 to =0
make sure that tblCondition table conditions are inserted from biggest (500) to lowest (0)
First what I do is create CASE for every row from tblCondition table.
Then I SUM data to temp table by PartCode (I split PartCode 'c' to 2 rows for testing)
And for the last, create dynamic code, which will update data
/*
CREATE TABLE #tblSale ( PartCode VARCHAR(10), PartGroup VARCHAR(10), SaleQty INT)
INSERT INTO #tblSale SELECT 'a', 'FM', 600
INSERT INTO #tblSale SELECT 'b', 'MM', 202
INSERT INTO #tblSale SELECT 'c', 'SM', 5
INSERT INTO #tblSale SELECT 'd', 'NM', 0
INSERT INTO #tblSale SELECT 'c', 'SM', 5
CREATE TABLE #tblCondition ( PartGroup VARCHAR(10), Condition VARCHAR(10))
INSERT INTO #tblCondition SELECT 'FM', '>500'
INSERT INTO #tblCondition SELECT 'MM', '>=200'
INSERT INTO #tblCondition SELECT 'SM', '>=1'
INSERT INTO #tblCondition SELECT 'NM', '=0'
*/
--CREATE CASES
DECLARE #CaseStr NVARCHAR(1000) = 'CASE '
SELECT #CaseStr = #CaseStr + '
WHEN SaleSUM ' + Condition + ' THEN '''+ PartGroup + ''' '
FROM #tblCondition
SET #CaseStr = #CaseStr + ' END'
-- SUM data by PartCode
SELECT PartCode, SUM(SaleQty) AS SaleSUM
INTO #tblSaleSUM
FROM #tblSale
GROUP BY PartCode
-- Create dynamic code for update
DECLARE #query NVARCHAR(MAX)
SET #query = N'
UPDATE S
SET S.PartGroup = SS.PartGroup
FROM #tblSale AS S
INNER JOIN
(
SELECT PartCode, ' + #CaseStr + ' AS PartGroup
FROM #tblSaleSUM
) AS SS
ON SS.PartCode = S.PartCode
'
EXEC sp_executesql #query
Use this
update ts set partGroup = something
from tblSale ts
inner join tblCondition tc
on tc.PartGroup=ts.PartGroup
inner join (Select PartGroup, sum(SaleQty) as SumSaleQty
from tblSale
group by PartGroup) as sums
on sums.PartGroup = tc.PartGroup
and sums.SumSaleQty >= tc.Condition

sql query including month columns?

I was wondering if I could get some ideas or direction on a sql query that would output column months. Here is my current query..
select A.assetid, A.Acquisition_Cost, B.modepreciaterate from FA00100 A
inner join FA00200 B on A.assetindex = B.assetindex
where MoDepreciateRate != '0'
I would like to add more columns that look as such:
select assetid, acquisition_cost, perdeprrate, Dec_2012, Jan_2013, Feb_2013....
where Dec_2012 = (acquisition_cost - MoDepreciateRate*(# of months))
and Jan_2013 = (acquisition_cost - MoDepreciateRate*(# of months))
where # of months can be changed.
Any help would be really appreciated. Thank you!
Here is an example of what I would like the output to be with '# of months' = 4
assetid SHRTNAME Acquisition_Cost perdeprrate Dec_2012 Jan_2013 Feb_2013 Mar_2013
CS-013 GEH INTEG 17490.14 485.83 17004.31 16518.48 16032.65 15546.82
CS-014 WEB BRD 14560 404.4507 14155.5493 13751.0986 13346.6479 12942.1972
Try This:
--setup
create table #fa00100 (assetId int, assetindex int, acquisitionCost int, dateAcquired date)
create table #fa00200 (assetIndex int, moDepreciateRate int, fullyDeprFlag nchar(1), fullyDeprFlagBit bit)
insert #fa00100
select 1, 1, 100, '2012-01-09'
union select 2, 2, 500, '2012-05-09'
insert #fa00200
select 1, 10, 'N', 0
union select 2, 15, 'Y', 1
.
--solution
create table #dates (d date not null primary key clustered)
declare #sql nvarchar(max)
, #pivotCols nvarchar(max)
, #thisMonth date
, #noMonths int = 4
set #thisMonth = cast(1 + GETUTCDATE() - DAY(getutcdate()) as date)
select #thisMonth
while #noMonths > 0
begin
insert #dates select DATEADD(month,#noMonths,#thisMonth)
set #noMonths = #noMonths - 1
end
select #sql = ISNULL(#sql + NCHAR(10) + ',', '')
--+ ' A.acquisitionCost - (B.moDepreciateRate * DATEDIFF(month,dateAcquired,''' + convert(nvarchar(8), d, 112) + ''')) ' --Original Line
+ ' case when A.acquisitionCost - (B.moDepreciateRate * DATEDIFF(month,dateAcquired,''' + convert(nvarchar(8), d, 112) + ''')) <= 0 then 0 else A.acquisitionCost - (B.moDepreciateRate * DATEDIFF(month,dateAcquired,''' + convert(nvarchar(8), d, 112) + ''')) end ' --new version
+ quotename(DATENAME(month, d) + '_' + right(cast(10000 + YEAR(d) as nvarchar(5)),4))
from #dates
set #sql = 'select A.assetid
, A.acquisitionCost
, B.moDepreciateRate
,' + #sql + '
from #fa00100 A
inner join #fa00200 B
on A.assetindex = B.assetindex
where B.fullyDeprFlag = ''N''
and B.fullyDeprFlagBit = 0
'
--nb: B.fullyDeprFlag = ''N'' has double quotes to avoid the quotes from terminating the string
--I've also included fullyDeprFlagBit to show how the SQL would look if you had a bit column - that will perform much better and will save space over using a character column
print #sql
exec(#sql)
drop table #dates
.
--remove temp tables from setup
drop table #fa00100
drop table #fa00200