How to find integer values within a String in SQL Server - sql-server

I am trying to calculate tenure in years based on data in this format 2 year(s), 11 month(s), 5 day(s). Below is the logic in oracle. I am trying to implement similar solution in SQL Server. Can you please help?
In this example 2 year(s), 11 month(s), 5 day(s) is converted to 2.93 years.
round(
(regexp_substr(tenure_in_current_job,'[^ ]+')
+ REGEXP_REPLACE(tenure_in_current_job,'(.*r\(s\), )(\d+)(.*)','\2')/12
+ REGEXP_REPLACE(tenure_in_current_job,'(.*h\(s\), )(\d+)(.*)','\2')/365),
2 )

select sum(num.val * 1.0 / case when trm.val like '%year%' then 1 when trm.val like '%month%' then 12 else 365 end)
from string_split('2 year(s), 11 month(s), 5 day(s)',',')
cross apply (select ltrim("value")) trm(val)
cross apply (select cast(left(trm.val,charindex(' ',trm.val)-1) as int)) num(val)
--------------
2.930365296802
Split the string on commas
Remove leading spaces on each of the 3 resulting rows
Remove everything from the first space onward, and cast as int
Divide the resulting int by 1, 12, or 365 as appropriate
Caveat: string_split was introduced in SQL Server 2016. If you're on SQL 2012 or older, this solution won't work.

Related

How to combine group by, join, COUNT, SUM and subquery clauses in sql

I am not sure how to write the SQL query for the following problem:
There are two tables, Worker and Product (one worker can make many products) which I describe in this link:https://docs.google.com/spreadsheets/d/1Yk2vKKmUEyuN-QfgTEbmF4suHFtuDkkrsUf-wqvOoKQ/edit?fbclid=IwAR3ipjwNrfhGXg3fCyAri4tD1Q4WqWuKVAqagvbsZg9Sn1myDwkWbWcl_6E#gid=0
The calculation of the total salary of a worker at month x is as follows
totalSalary = salaryPerMonth + SUM(salaryPerProduct * COUNT(pid))
I want to use join statement (regardless of INNER JOIN, LEFT, OR RIGHT JOIN) combined with group by clause to solve this problem but my statements are wrong.
Expect a specific SQL statement in this case.
I hope to be able to express my ideas in this photo
UPDATE: my picture quality is not good so i will repost my picture on this linkenter image description here
#phi nguyễn quốc - Welcome to StackOverflow. What you posted has the makings of a good question. It contains:
Brief summary of the issue
Table structure, sample data
Explanation of expected results
Code you've tried
It just needs a few modifications to conform to the guidelines and avoid being closed. A few tips on posting:
Help others to help you by including a Minimal, Reproducible Example. (With SQL questions include table definitions and sample data). That way folks who want to help can spend their time answering your question, instead of on writing set-up code to replicate your tables, environment, etc..
Make it easy for others to be able to test your code. Always post code as text, not as an image.
Use collaborative tools like db<>fiddle for sharing
One example of how you might improve the question and avoid it being closed:
Issue:
I am trying to write a SQL query to calculate the total salary for workers for a given month X. There are two tables: [Worker] and [Product]. One worker can make many products.
wid
wname
salaryPerMonth
salaryPerProduct
phoneNumber
1
Mr A
500
5
2
Mr B
100
30
3
Mr C
200
20
pid
pname
manufacturedDate
wid
1
Product A
2013-12-01
1
2
Product B
2013-12-09
1
3
Product C
2013-09-08
1
4
Product D
2013-01-30
2
5
Product E
2013-09-20
2
6
Product F
2013-12-23
3
The "Total Salary" of a worker for month X is calculated as follows:
SalaryPerMonth +
( SalaryPerProduct *
Number of Products for Month
)
Expected Results: (December 2013)
wid
wname
salaryPerMonth
salaryPerProduct
totalSalary
** Formula
1
Mr A
500
5
510
= 500 + (5*2)
2
Mr B
100
30
100
= 100 + (30*0)
3
Mr C
200
20
220
= 200 + (20*1)
Actual Results
I've tried this query
SELECT W.wid, W.wname, W.phoneNumber, W.salaryPerMonth, W.salaryPerProduct, (W.salaryPerMonth - SUM(W.salaryPerMonth*COUNT(p.pid))) AS Total
FROM Worker W INNER JOIN Product P ON p.Wid = W.wid
WHERE MONTH(P.manufacturedDate) = 12
GROUP BY W.wid, W.wname, W.phoneNumber, W.salaryPerMonth, W.salaryPerProduct
.. but am getting the error below:
Msg 130 Level 15 State 1 Line 1
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
Here is my db<>fiddle
CREATE TABLE Product (
pid int
, pname varchar(40)
, manufacturedDate date
, wid int
);
CREATE TABLE Worker (
wid int
, wname varchar(40)
, salaryPerMonth int
, salaryPerProduct int
, phoneNumber varchar(20)
)
INSERT INTO Product(pid, pname, manufacturedDate, wid)
VALUES
(1,'Product A','2013-12-01',1)
,(2,'Product B','2013-12-09',1)
,(3,'Product C','2013-09-08',1)
,(4,'Product D','2013-01-30',2)
,(5,'Product E','2013-09-20',2)
,(6,'Product F','2013-12-23',3)
;
INSERT INTO Worker (wid, wname, salaryPerMonth,salaryPerProduct)
VALUES
(1,'Mr A', 500, 5)
,(2, 'Mr B', 100, 30)
,(3,'Mr C', 200, 20)
;

How to find the number of days between two different dates in SQL Server? [duplicate]

This question already has answers here:
SQL SERVER: Get total days between two dates
(9 answers)
Closed 3 years ago.
I have basic query:
SELECT A.Username
,EH.[From]
,EH.[End]
,DATEDIFF(d, [From], [End]) AS HolidaysInDays
FROM EmployeHoliday EH
LEFT JOIN Admin A ON EH.UserId = A.AdminId
WHERE EH.IsActive = 1
If employee apply leave(LeaveStart = "17 Dec 2019" to LeaveEnd = "19 Dec 2019"),
when I execute my query the output is HolidaysInDays = 2 but the actual Days is 3. So, how can I resolve this problem?
My output is as shown in the below image.
When using DATEDIFF, the end date is an exclusive date so is not factored into the calculation:
PRINT DATEDIFF(d, '2019-12-17', '2019-12-19')
Output:
2
To add the final date in, just add 1 to the result:
PRINT DATEDIFF(d, '2019-12-17', '2019-12-17') + 1
PRINT DATEDIFF(d, '2019-12-17', '2019-12-18') + 1
PRINT DATEDIFF(d, '2019-12-17', '2019-12-19') + 1
Output:
1
2
3
Think of it like this:
what is 7 - 5?
Boom.

Expression to find value for previous year SSRS

I need some help with expressions.I need a report that shows calcul of a field for current year as well as previous year respecting this rule expression(Last-Last Previous)/Last Previous*100. Also the report runs on a Year Parameter.
Below is an example for the result
for example i selected the years 2010 2011 2012
year
Data 2010 2011 2012
hp 14 25 30
Dell 17 18 20
and the result i want
year
Data 2010 2011 2012 2011/2012
hp 14 25 30 0.002 (Last -Last Previous)/(last Previous*100) =(30-25)/(25*100)
Dell 17 18 20 0.0040
How can i do this
SELECT
NON EMPTY
{[Measures].[Val]} ON COLUMNS
,NON EMPTY
{
[DimCAT].[lbl].[lbl].ALLMEMBERS * [DimDate].[Year].[Year].ALLMEMBERS
} ON ROWS
FROM [Data];
Via mdx you need to use a WITH clause.
The calculated member that is added is called 2011/12 but is actually the last member of the year hierarchy compared to the previous year - so maybe not exactly what you require:
WITH
MEMBER [DimDate].[Year].[2011/12] AS
(
Tail([DimDate].[Year].[Year].ALLMEMBERS).Item(0).Item(0)
-
Tail([DimDate].[Year].[Year].ALLMEMBERS).Item(0).Item(0).PrevMember
)
/
Tail([DimDate].[Year].[Year].ALLMEMBERS).Item(0).Item(0).PrevMember
* 100
SELECT
NON EMPTY
{
[DimDate].[Year].[Year].ALLMEMBERS
,[DimDate].[Year].[2011/12]
} ON COLUMNS
,NON EMPTY
{[DimCAT].[lbl].[lbl].ALLMEMBERS} ON ROWS
FROM [Data]
WHERE
[Measures].[Val];
Using the strToSet function you could get a single member set using the parameter:
WITH
SET [s] AS
StrToSet('{[DimDate].[Year].[Year].[' + #Yr + ']}')
MEMBER [DimDate].[Year].[LastVsPrev] AS
([s].Item(0).Item(0) - [s].Item(0).Item(0).PrevMember)
/
[s].Item(0).Item(0).PrevMember
* 100
SELECT
NON EMPTY
Union
(
{
[s].Item(0).Item(0).PrevMember.PrevMember : [s].Item(0).Item(0)
}
,{[DimDate].[Year].[LastVsPrev]}
) ON COLUMNS
,NON EMPTY
{[DimCAT].[lbl].[lbl].ALLMEMBERS} ON ROWS
FROM [Data]
WHERE
[Measures].[Val];
If you have the chance to change the source dataset i would bring the dataset with the year UNPIVOTed and with a Row_Number on Data with year descending order. That way you can filter the last two years you want.
(this was tested in SSRS 2008 there might be easier ways especially in later versions)
select
Data,[year],measure,ROW_NUMBER() over (partition by data order by [year] desc) i
from (values
('hp',2010,14),
('hp',2011,25),
('hp',2012,30),
('Dell',2010,17),
('Dell',2011,18),
('Dell',2012,20)) t (data,year,measure)
Data year measure i
hp 2010 14 3
hp 2011 25 2
hp 2012 30 1
Dell 2010 17 3
Dell 2011 18 2
Dell 2012 20 1
And do a Column Group on year and a row group on data.
Expression SUM(Field!Measure.Value) on the intersecting cell of the 2 groups.
Add Column after column group and add the Expression to the Header cell
=MAX(iif(Fields!i.Value = 2,Fields!year.Value.ToString(),""))+"/"+ MAX(iif(Fields!i.Value = 1,Fields!year.Value.ToString(),""))
and the next expression to the Cell
=iif(SUM(iif(Fields!i.Value = 2,Fields!measure.Value,0))=0,0,SUM(iif(Fields!i.Value = 1,Fields!measure.Value,0))-SUM(iif(Fields!i.Value = 2,Fields!measure.Value,0)) / (SUM(iif(Fields!i.Value = 2,Fields!measure.Value,0))*100))
I think the formula is OK, but it's returning different values than what you posted as expected.

In SSRS, how can I add a row to aggregate all the rows that don't match a filter?

I'm working on a report that shows transactions grouped by type.
Type Total income
------- --------------
A 575
B 244
C 128
D 45
E 5
F 3
Total 1000
I only want to provide details for transaction types that represent more than 10% of the total income (i.e. A-C). I'm able to do this by applying a filter to the group:
Type Total income
------- --------------
A 575
B 244
C 128
Total 1000
What I want to display is a single row just above the total row that has a total for all the types that have been filtered out (i.e. the sum of D-F):
Type Total income
------- --------------
A 575
B 244
C 128
Other 53
Total 1000
Is this even possible? I've tried using running totals and conditionally hidden rows within the group. I've tried Iif inside Sum. Nothing quite seems to do what I need and I'm butting up against scope issues (e.g. "the value expression has a nested aggregate that specifies a dataset scope").
If anyone can give me any pointers, I'd be really grateful.
EDIT: Should have specified, but at present the dataset actually returns individual transactions:
ID Type Amount
---- ------ --------
1 A 4
2 A 2
3 B 6
4 A 5
5 B 5
The grouping is done using a row group in the tablix.
One solution is to solve that in the SQL source of your dataset instead of inside SSRS:
SELECT
CASE
WHEN CAST([Total income] AS FLOAT) / SUM([Total income]) OVER (PARTITION BY 1) >= 0.10 THEN [Type]
ELSE 'Other'
END AS [Type]
, [Total income]
FROM Source_Table
See also SQL Fiddle
Try to solve this in SQL, see SQL Fiddle.
SELECT I.*
,(
CASE
WHEN I.TotalIncome >= (SELECT Sum(I2.TotalIncome) / 10 FROM Income I2) THEN 10
ELSE 1
END
) AS TotalIncomePercent
FROM Income I
After this, create two sum groups.
SUM(TotalIncome * TotalIncomePercent) / 10
SUM(TotalIncome * TotalIncomePercent)
Second approach may be to use calculated column in SSRS. Try to create a calculated column with above case expression. If it allows you to create it, you may use it in the same way as SQL approach.
1) To show income greater than 10% use row visibility condition like
=iif(reportitems!total_income.value/10<= I.totalincome,true,false)
here reportitems!total_income.value is total of all income textbox value which will be total value of detail group.
and I.totalincome is current field value.
2)add one more row to outside of detail group to achieve other income and use expression as
= reportitems!total_income.value-sum(iif(reportitems!total_income.value/10<= I.totalincome,I.totalincome,nothing))

Utilizing SQL datepart to indentify consecutive periods of time

I have a stored procedure that works correctly, but don't understand the theory behind why it works. I'm indentifying a consecutive period of time by utilizing a datepart and dense rank (found solution through help elsewhere).
select
c.bom
,h.x
,h.z
,datepart(year, c.bom) * 12 + datepart(month, c.bom) -- this is returning a integer value for the year and month, allowing us to increment the number by one for each month
- dense_rank() over ( partition by h.x order by datepart(year, c.bom) * 12 + datepart(month, c.bom)) as grp -- this row does a dense rank and subtracts out the integer date and rank so that consecutive months (ie consecutive integers) are grouped as the same integer
from
#c c
inner join test.vw_info_h h
on h.effective_date <= c.bom
and (h.expiration_date is null or h.expiration_date > c.bom)
I understand in theory what is happening with the grouping functionality.
How does multiplying year * 12 + month work? Why do we multiply the year? What is happening in the backend?
The year component of a date is an integer value. Since there are 12 months in a year, multiplying the year value by 12 provides the total number of months that have passed to get to the first of that year.
Here's an example. Take the date February 11, 2012 (20120211 in CCYYMMDD format)
2012 * 12 = 24144 months from the start of time itself.
24144 + 2 months (february) = 24146.
Multiplying the year value by the number of months in a year allows you to establish month-related offsets without having to do any coding to handle the edge cases between the end of one year and the start of another. For example:
11/2011 -> 24143
12/2011 -> 24144
01/2012 -> 24145
02/2012 -> 24146

Resources