Where clause on numbers dont give expected results - sql

I have the following sample data in table_a.value:
abcdef 10 / 20 / 30 adfadsf
adfadsf 1000 / 10,5 / 300.5 kjbkjj
adsfadsf 0.1 / 8000 / 0,0005 asdfdasf
adsfasdf dfkjaf dsaflkjadslf asdfasdf 100 / 10.5 dslfjalksdf 500
adfdasf 50 sdlfkja 1000 alfdkjasf 50.5
ajkfdha asfdjlas dslkfjsdf
I want to filter out with a where clause row 1 to 3 where it has the following condition:
where table_a.value like '%number / number / number%'.
Expected results would be:
abcdef 10 / 20 / 30 adfadsf
adfadsf 1000 / 10,5 / 300.5 kjbkjj
adsfadsf 0.1 / 8000 / 0,0005 asdfdasf
I tried to make it work with the following code:
Where table_a.value like '%[0-9.,] / [0-9.,] / [0-9.,]%'
However, this is not giving me the expected results.
Could somebody guide me in the right direction?

SQL Server is not very good at doing this. You might be able to simplify your problem. For instance, you can get the same rows just by looking for two slashes surrounded by spaces:
where a_value like '% / % / %'
You can ensure that the central component is a number and that there are numbers before and after the spaces:
where a_value like '%[0-9] / [0-9]% / [0-9]%' and
a_value not like '%[0-9] / [0-9]%[^0-9]% / [0-9]%'
This is not 100% equivalent to what you want to do, but it might be sufficient for your purposes.

SQL Server does not expose any true regular expression functionality via TSQL. The pattern syntax does not have any sort of support for quantifiers.
In this case you could first use TRANSLATE to ensure that all characters in the set 0-9., are denoted as 0 and then use a couple of nested replaces to collapse down contiguous series of 0 to be represented as 12. (REPLACE(REPLACE(..., '0', '12'),'21',''))
The initial TRANSLATE ensures the input to that step can't have any other 1 or 2 characters.
Then check that the result is LIKE '%12 / 12 / 12%'
SELECT *
FROM ( VALUES ('abcdef 10 / 20 / 30 adfadsf'),
('adfadsf 1000 / 10,5 / 300.5 kjbkjj'),
('adsfadsf 0.1 / 8000 / 0,0005 asdfdasf'),
('adsfasdf dfkjaf dsaflkjadslf asdfasdf 100 / 10.5 dslfjalksdf 500'),
('adfdasf 50 sdlfkja 1000 alfdkjasf 50.5'),
('ajkfdha asfdjlas dslkfjsdf') )V(Col)
WHERE REPLACE(REPLACE(TRANSLATE(Col, '123456789.,', '00000000000'), '0', '12'), '21', '') LIKE '%12 / 12 / 12%'
(Fiddle)

Related

SQL ending price with 5

I'm having a problem with a round.
I need to round prices to end with 5.
Example 1: 647,927 needs to end with 5 with tax. So it should be 815.
647,927 * 1,25 (tax) is 809,90 how can i round 809,90 up to 815
Example 2: 283,30 needs also to end with 5 with tax. So it should be 355.
283,30 * 1,25 (tax) is 354,125 how can i round 354,125 up to 355?
You can use arithmetic. Something like:
select ceiling( (val - 5) / 10 ) * 10 + 5
Here is a db<>fiddle for your two examples.

SQL Server wrong mathematical result

I have this statement:
Select (30 - 5) * 700 / 30 as A , 700 - (5 * 700 / 30) as B
which has two ways to calculate the same equation.
These two equations should result in 583.33 if you made them by your calculator
but the previous statement results in 583 for field A, and 584 for field B.
Both are wrong and both are integer not decimal.
I want to know what's the right way to write this statement so I can get 583.33.
Thanks
Your expression is implicitly using an INT datatype. Try it this way to allow it to use a DECIMAL datatype
Select (30.000 - 5.000) * 700.000 / 30.000 as A ,
700.000 - (5.000 * 700.000 / 30.000) as B
Use Cast function:
As next:
Select Cast((30 - 5) * 700 as DECIMAL(19,2) ) / 30 as A ,
700 - ( Cast (5 * 700 as decimal (19,2 )) / 30) as B
Result:
A B
583.333333 583.333334

SQL group by steps

I'm using SQL in SAS.
I'm doing a SQL query with a GROUP BY clause on a continuous variable (made discrete), and I'd like it to aggregate more. I'm not sure this is clear, so here is an example.
Here is my query :
SELECT CEIL(travel_time) AS time_in_mn, MEAN(foo) AS mean_foo
FROM my_table
GROUP BY CEIL(travel_time)
This will give me the mean value of foo for each different value of travel_time. Thanks to the CEIL() function, it will group by minutes and not seconds (travel_time can take values such as 14.7 (minutes)). But I'd like to be able to group by groups of 5 minutes, for instance, so that I have something like that :
time_in_mn mean_foo
5 4.5
10 3.1
15 17.6
20 12
(Of course, the mean(foo) should be done over the whole interval, so for time_in_mn = 5, mean_foo should be the mean of foo where travel_time in (0,1,2,3,4,5) )
How can I achieve that ?
(Sorry if the answer can be found easily, the only search term I could think of is group by step, which gives me a lot of "step by step tutorials" about SQL...)
A common idiom of "ceiling to steps" (or rounding, or flooring, for that matter) is to divide by the step, ceil (or round, or floor, of course) and then multiply by it again. This way, if we take, for example, 12.4:
Divide: 12.4 / 5 = 2.48
Ceil: 2.48 becomes 3
Multiply: 3 * 5 = 15
And in SQL form:
SELECT 5 * CEIL(travel_time / 5.0) AS time_in_mn,
MEAN(foo) AS mean_foo
FROM my_table
GROUP BY 5 * CEIL(travel_time / 5.0)

SQL server generate number from 1 to nth

I have a table like;
**ID** **CASH** **INTERVAL**
1 60 5
2 10 3
3 20 4
I want to add 2 columns deriving from current ones like; Column MULT means I list numbers from 1 to INTERVAL by commas and for VAL value I substract CASH from 100 and divide it by INTERVAL and list those intervals by comma listed values inside column VAL
**ID** **CASH** **INTERVAL** **MULT** **VAL**
1 60 5 1,2,3,4,5 8,8,8,8,8
2 10 3 1,2,3 30,30,30
3 20 4 1,2,3,4 20,20,20,20
I know it looks like not an informative question but at least anyone know about to list them in single column with commas using STUFF or etc?
Given how you phrase the question and the sample data you provide, I would be tempted to use a very bespoke approach for this:
with params as (
select '1,2,3,4,5,6,7,8,9' as numbers,
'x,x,x,x,x,x,x,x,x' as vals
)
select l.*,
left(numbers, interval * 2 - 1) as mult,
replace(left(vals, interval * 2 - 1), 'x', (100 - cash) / interval) as val
from params cross join
[like] l;
Of course, you might need to extend the strings in the CTE, if they are not long enough (and this might affect the arithmetic).
The advantage to this approach is speed. It should be pretty fast.
Note: you can also use replicate() rather than the vals.

Why is this SQL SUM statement correct?

In my schema, I have a table Projects, and a table Tasks. Each project is comprised of tasks. Each task has Hours and PercentComplete.
Example table:
ProjectID TaskID Hours PercentComplete
1 1 100 50
1 2 120 80
I am trying to get the weighted percentage complete for the project. I am doing this using the following SQL statement:
SELECT P.ProjectID, P.ProjectName, SUM(T.Hours) AS Hours,
SUM(T.PercentComplete * T.Hours) / 100 AS CompleteHours,
SUM(T.PercentComplete * T.Hours) / SUM(T.Hours) AS PercentComplete
FROM Projects AS P INNER JOIN
Tasks AS T ON T.ProjectID = P.ProjectID
WHERE (P.ProjectID = 1)
My question is about this part of that statement:
SUM(T.PercentComplete * T.Hours) / SUM(T.Hours) AS PercentComplete
This gives me the correct weighted percentage for this project (in the case of the sample data above, 66%). But I cannot seem to wrap my head around why it does this.
Why does this query work?
SUM(T.PercentComplete * T.Hours) / 100 is the number of complete hours.
SUM(T.Hours) is the total number of hours.
The ratio of these two amounts, i.e.:
(SUM(T.PercentComplete * T.Hours) / 100) / SUM(T.Hours)
is the proportion of hours complete (it should be between 0 and 1).
Multiplying this by 100 gives the percentage.
I prefer to keep percentages like this out of the database and move them to the presentation layer. It would be much easier if the database stored "hours completed" and "hours total" and did not store the percentages at all. The extra factors of 100 in the calculations confuse the issue.
Basically you are finding the number of hours completed over the number of hours total.
SUM(T.PercentComplete * T.Hours) computes the total number of hours that you have completed. (100 * 50) = 50 * 100 + (120 * 80) = 146 * 100 is the numerator. 146 hours have been completed on this job, and we keep a 100 multiplier for the percent (because it is [0-100] instead of [0-1])
Then we find the total number of hours worked, SUM(T.Hours), which is 100 + 120 = 220.
Then dividing, we find the weighted average. (146 * 100) / 220 = 0.663636364 * 100 = 66.4%
Is this what you were wondering about?
It calculates the two sums individually by adding up the value for each row then divides them at the end
SUM(T.PercentComplete * T.Hours)
50* 100 +
80 * 120
-------
14,600
SUM(T.Hours)
100 +
120
---
220
Then the division at the end
14,600 / 220
------------
66.3636
Edit As per HLGEM's comment it will actually return 66 due to integer division.
Aggregate functions, such as SUM(), work against the set of data defined by the GROUP BY clause. So if you group by ProjectID, ProjectName, the functions will break things down by that.
The SUM peratiorn first multiply the columns than add
( 100* 50+ 120* 80) / (100+ 120)