finding range by comparing two tables - sql

I have a table in database as "EXPERIENCE RANGE" with rows as (I can also edit this table according to my need)
0
0.5
1
2
3
5
10
20
I have total experience as integer. I need to display the range in which it lies.
Example - for experience of 8, Range will be 5 - 10
I need to write a sql query. Any ideas will be quite helpful as I am new to SQL.
I cannot hard code it..need to take values from tables only.

Assuming that you are using Oracle, the following query works fine with your existing table:
SELECT
( SELECT MAX( value ) FROM experience_range WHERE value <= :search_value ) AS range_start,
( SELECT MIN( value ) FROM experience_range WHERE value > :search_value ) AS range_end
FROM dual;
No need to hardcode the values, and no need to store the lower and upper bounds redundantly.

you can do it with CASE Expression, the syntax is:
SELECT
CASE
WHEN experience >= 0 and experience <= 4 THEN '0-4'
WHEN experience >= 5 and experience <= 10 THEN '5-10'
.....
ELSE 'No Range'
END as Range
FROM Table_Name

If you do need to store the ranges in a table, I would personally suggest altering the structure of the range table (Assuming you are able to), maybe something like:
|--------------------------------------|
|ID|DESCRIPTION|LOWER_LIMIT|UPPER_LIMIT|
|1 | 0 - 0.5 | 0 | 0.5 |
|2 | 0.5 - 1 | 0.5 | 1 |
...
Then you could get your range by running something like:
SELECT DESCRIPTION FROM [RANGES] WHERE <VALUE> >= LOWER_LIMIT AND <VALUE> < UPPER_LIMIT
EDIT - Mikhail's answer also works, defining the ranges within the query itself is also an option and probably simpler providing you don't need to get these ranges from several reports. (That would require editing every report/query individually)
EDIT 2 - I see you are not able to hardcode the ranges, in which case the above would be best. Can I ask why you are unable to hardcode them?

Related

Functionality Similar to SUMIFS with DAX?

Sample workbook: http://1drv.ms/1VDgAjf
I've got a table similar to:
ActiveDate CommenceDate Amount
-------------------------------------------
20150115 20150201 10
20150115 20150201 2
20150223 20150301 3
20150223 20150202 5
I need to calculate the following:
Date Amount
---------------------
25-Jan-15 0
30-Jan-15 0
04-Feb-15 12
09-Feb-15 12
14-Feb-15 12
19-Feb-15 12
24-Feb-15 17
01-Mar-15 20
06-Mar-15 20
11-Mar-15 20
So.. in Excel I've tested this with the following statement:
=SUMIFS(
Table[amount]
,Table[commence] ,"<="&TEXT(<<DateRef>>, "yyyymmdd")
,Table[active] ,"<="&TEXT(<<DateRef>>, "yyyymmdd")
)
This works fine.. my question is, how do i replicate this in DAX?
Here is my best stab (assuming a date dimension, and it connected to "CommenceDate"):
TotalAmount :=
CALCULATE (
SUM ( Table[Amount] ),
FILTER (
ALL ( 'Date'[Date] ),
'Date'[Date] <= MAX ( 'Date'[Date] )
)
)
My best idea (and i think it's a pretty crappy idea), is to add a new column that gives me the greater of ActiveDate, or CommenceDate and then use an in-active relationship to join to that, and make the relationship active just for this calculation?
=IF([#active]>[#commence], [active], [commence])
Thoughts?
Your thought of creating an additional column is probably the best in this case. No matter what you do, you'll have to escape the active relationship between Transactions[commence] and DimDate[DateKey] to get this logic working correctly.
After doing that manipulation, you'll then have to filter on two columns ([active], [commence]) against the current date in context. This will be more cumbersome and less efficient than navigating a single relationship and using a single filter.
The thing to keep in mind is that you need to apply the relationship manipulation step before, not with the filter manipulation step. This looks like the following nested CALCULATE():
TotalAmount:=
CALCULATE(
CALCULATE(
SUM(Transactions[Amount])
,USERELATIONSHIP(Transactions[MaxDateKey], DimDate[DateKey])
)
,FILTER(
ALL('Date')
,'Date'[Date] <= MAX('Date'[Date])
)
)

SQL - Min difference between two integer fields

How I can get min difference between two integer fields(value_0 - value)?
value_0 >= value always
value_0 | value
-------------------
15 | 10
12 | 10
15 | 11
11 | 11
Try this:
SELECT MIN(value_0-value) as MinDiff
FROM TableName
WHERE value_0>=value
With the sample data you have given,
Output is 0. (11-11)
See demo in SQL Fiddle.
Read more about MIN() here.
Here is one way:
select min(value_0 - value)
from table t;
This is pretty basic SQL. If you want to see other values on the same row as the minimum, use order by and choose one row:
select (value_0 - value)
from table t
order by (value_0 - value)
limit 1;
The limit 1 works in some databases for getting one row. Others use top 1 in the select clause. Or fetch first 1 rows only. Or even something else.

Select column based on another column's value - SQL

I have a table of rates and terms from which I need to use the term to select the appropriate rate adjustment. The issue is, each term is its own column as so:
term 12mon 24mon 36mon
----- ----- ----- -----
12 2 4 6
24 2 4 6
I need, for each term, to return the correct adjuster.
Thus for 12 months I would need a "2" and for 24 it would be "4" and so on. While vastly oversimplified it captures the essence - I need to select a column name in a table based upon the value of another column in the same table.
I'm not able to change the source table.
Thanks in advance...
case is your friend
case term
when 12 then [12mon]
when 24 then [24mon]
when 36 then [36 mon]
end as rate
if value of term can be between 12 and 24, etc. then write it this way (I'm not sure what your logic needs to be, but you get the idea)
case
when term < 12 then 0
when term < 24 then [12mon]
when term < 36 then [24mon]
when term < 48 then [36mon]
else [48mon]
end as rate
What flavor of SQL is that ?
In most of them you can use CASE WHEN (predicate) THEN x statements which you can use to get different columns and then alias it.

SQL COUNT between dates in two different column

Let's say, we have this table:
STUDENT | START | END
1 |1998-1-1 |2001-1-1
2 |1999-1-1 |2001-1-1
3 |2000-1-1 |2004-1-1
4 |2000-1-1 | NULL
I'm trying to do is:
Count number of students between start and end dates!
Looks like you need to use a basic COUNT aggregate:
SELECT COUNT(Student)
FROM YourTable
WHERE Start >= #Start
AND End <= #End
I've used >= and <= respectively around the start and end date fields. Feel free to change to > or < as needed. It was unclear from your question whether you wanted between a specific field or if you were checking for a range between those two fields.
Use the between Operator and COUNT aggregate function
SELECT COUNT(student) column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
Between can be used with text so insert the dates where the values are,
Read more here if you still don't understand
EDIT : That should work, sorry about the error
http://www.w3schools.com/sql/sql_between.asp

How to select based on maximum time interval

I have a table as follows in Postgres SQL 8.4:
1 | John Smith | 2011-08-12 12:44:13.125+08
2 | John Smith | 2011-08-16 08:38:57.968+08
3 | John Smith | 2011-08-16 08:38:58.062+08
4 | Kenny Long | 2011-08-16 17:06:35.843+08
5 | Kenny Long | 2011-08-16 17:06:35.906+08
6 | Kenny Long | 2011-08-16 17:06:59.281+08
7 | Kenny Long | 2011-08-16 17:07:00.234+08
8 | Kenny Long | 2011-08-16 17:07:32.859+08
9 | Kenny Long | 2011-08-16 17:08:00.437+08
10 | Kenny Long | 2011-08-16 17:08:22.718+08
11 | Kenny Long | 2011-08-16 17:08:22.781+08
I would like to select the columns based timestamp. Only one row is needed for those records that fall within 2 minutes from each other. For example records number 4 to 9 should return only row number 4 and ignore the rest of the rows.
How can I achieve this? Your help is greatly appreciated.
Thank you in advance.
Joe Liew
I've tried it with a recursive way. I'm not sure it's the better way, and I'm quite sure I should study some Window operations to reduce it.
But It worked on my test case. The goal is to start with one min timestamp per guy, then track which rows are to be deleted (within the 2 min range), and which row is the next valid one. Then at each iteration we continue from this valid row (one per guy).
So here is the query for table myschema.mytable with colums id,name,tm. Note that the level column is there just to track recursion and debug, not necessary:
WITH RECURSIVE mytmp(id,name,thetime,thelevel) AS (
-- recursive query: 1st row
-- starting point, one row of the table for each people
-- with a subquery to get the min time with id, maybe a better way to do it
(
select myschema.mytable.id,myschema.mytable.name,myschema.mytable.tm as thetime,1 as thelevel
from (
select name,min(tm) as mintm
from myschema.mytable
group by name
) q,myschema.mytable
WHERE myschema.mytable.name=q.name
AND myschema.mytable.tm=q.mintm
ORDER BY name ASC) -- end of starting point of recursive query
UNION ALL
-- now the recursive part, starting from the 1st row and then again and again (loop)
-- get descendants in the 2 minutes interval for every computed row already in mytmp
--
-- get from previous iterations targets, one per guy
-- and track the 1st new valid row (>2min) for that guy
-- removing bad rows (<2min) is easy, several way to do it
-- keeping only one valid row (and not all the others is harder, limit and aggregates functions are restricted in recursive terms
-- we must keep only one, as the future valid rows will depend on the 2 minutes range from this one
-- maybe some window function could help me, but at least I've a working solution
select myschema.mytable.id,myschema.mytable.name,myschema.mytable.tm as thetime,q2.thelevel
FROM myschema.mytable,(
-- here need to keep 1st true one
select myschema.mytable.name,MIN(myschema.mytable.tm) as tm,mytmp2.thelevel +1 as thelevel
FROM myschema.mytable,(
select id,name,thetime,thelevel
from mytmp
) mytmp2
-- hack: mytmp2 is useless, mytmp should have been used
-- we create this indirection to avoid this message:
-- "ERROR: aggregate functions not allowed in a recursive query's recursive term"
-- on the MIN functions
-- I do not know why it worked :-)
WHERE myschema.mytable.name=mytmp2.name
-- future
AND myschema.mytable.tm - mytmp2.thetime > INTERVAL '0'
GROUP BY
-- hack the group by, to make 2 groups
-- the first one for rows in the 2 min range and the second one for others
CASE WHEN ((myschema.mytable.tm - mytmp2.thetime) > INTERVAL '2 minutes') THEN 1 ELSE 2 END,
myschema.mytable.name,mytmp2.thelevel,mytmp2.thetime
-- then with the having we keep only the second group, containing the first valid > 2min row
HAVING ((MIN(myschema.mytable.tm) - mytmp2.thetime) > INTERVAL '2 minutes')=true
) q2 -- q2contains 1st true row and all false rows for each people
-- q2 is used to get the id, that we cannot have in a group by request
WHERE q2.tm=myschema.mytable.tm
AND q2.name=myschema.mytable.name
) -- end of recursive query
SELECT *
FROM mytmp
ORDER BY name asc, thelevel asc,thetime asc
-- LIMIT 100 -- to debug, avoid infinite loops
Another solution would maybe be using a stored procedure, doing the same things in a temporary table (take valid rows, delete rows in the 2min range, then take the next valid ones, etc), maybe easier to maintain.
Just some idea, not tested. Windowing function need 8.4 / later.
SELECT * FROM
(SELECT
name,
tm,
case
when lagname is NULL OR -- first row of everything
(name <> lagname) OR -- we have order by name, this is first row of this name
(name = lagname AND lagtm + interval '2 minutes' >= tm)
then 1
else 0
end as flags
FROM
(
SELECT name,
tm,
lag(name) over (order by name,tm) as lagname,
lag(tm) over (order by name,tm) as lagtm
from "table"."table"
) AS lagtable
) AS blar
WHERE "flags" = 1