Select column based on another column's value - SQL - 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.

Related

Confusing with Having query in sql

I am using sql server management studio 2012 and have to make a query to show which subject a student has failed(condition for failing is point<5.0) the most for the first time from this table
StudentID | SubjectID | First/Second_Time | Point.
1 | 02 | 1 | 5.0
2 | 04 | 2 | 7.0
3 | 03 | 2 | 9
... etc
Here are my teacher's query:
SELECT SubjectID
FROM Result(NAME OF the TABLE)
WHERE [First/Second_Time] = 1 AND Point < 5
GROUP BY SubjectID
HAVING count(point) >= ALL
(
SELECT count(point)
FROM Result
WHERE [First/Second_Time] = 1 AND point < 5
GROUP BY SubjectID
)
I don't understand the reason for making the having query. Because Count(point) is always >=all(select count(point)
from Result
where First/Second_Time=1 and point<5
group by SubjectID), isnt it ?
and it doesn't show that the subject has most student fail for the first time. Thanks in advance and sorry for my bad english
The subquery is returning a list of the number of times a subject was failed (on the first attempt). It might be easier for you to see what it's doing if you run it like this:
SELECT SubjectID, count(point)
FROM Result
WHERE [First/Second_Time] = 1 AND point < 5
GROUP BY SubjectID
So if someone failed math twice and science once, the subquery would return:
2
1
You want to know which subject was failed the most (in this case, which subject was failed 2 or more times, since that is the highest number of failures in your subquery). So you count again (also grouping by subject), and use having to return only subjects with 2 or more failures (greater than or equal to the highest value in your subquery).
SELECT SubjectID
FROM Result
WHERE [First/Second_Time] = 1 AND Point < 5
GROUP BY SubjectID
HAVING count(point)...
See https://msdn.microsoft.com/en-us/library/ms178543.aspx for more examples.
Sounds like you are working on a project for a class, so I'm not even sure I should answer this, but here goes. The question is why the having clause. Have you read the descriptions for having and all ?
All "Compares a scalar value with a single-column set of values".
The scalar value in this case is count(point) or the number of occurrences of a subject id with point less than 5. The single-column set in this case is a list of the number of occurrences of every subject that has less than 5 points.
The net result of the comparison is in the ">=". "All" will only evaluate to true if it is true for every value in the subquery. The subquery returns a set of counts of all subjects meeting the <5 and 1st time requirement. If you have three subjects that meet the <5 and 1st time criteria, and they have a frequency of 1,2,3 times respectively, then the main query will have three "having" results; 1,2,3. Each of the main query results has to be >= each of the subquery results for that main value to evaluate true. So going through step by step, First main value 1 is >= 1, but isn't >= 2 so 1 drops because the "having" is false. Second main value 2 is >=1, is >= 2, but is not >= 3 so it drops. Third value, 3, evaluates true as >= 1, 2, and 3, so you end up returning the subject with the highest frequency.
This is fairly clear in the "remarks" section of the MSDN discussion of "All" keyword, but not as relates to your specific application.
Remember, MSDN is our friend!

How to combine multiple condition in table join?

Simply speaking, I two rule tables, one lists all the rules, the other lists details of the rules:
Rule_ID Rule_Name
1 "Rule Name 1"
2 "Rule Name 2"
Target_Rule_ID Condition
1 >10
1 <20
1 !=15
1 !=18
2 >30
Meaning: for Rule_ID number 1, the value is more than 10, less then 20, and not eqaul to 15 nor 18.
I need to apply this rule to another data table, like:
ID Value
1 11
2 60
3 15
And make the result like:
ID Value Rule_ID
1 11 1
2 60 2
3 15 null
The current method I can think of is use a high level language like python.
get the rules all out
make the where clause
join the table one by one
But sounds inefficient, since that means I need to join the rule with the data table X times (X = total rule number).
I wonder is there a better way to do this directly in sql server? Any suggestions?
(Also assume the rules don't conflict with each other, that would make the problem even harder)...
Regardless of where you do it, you need a way to separate the numerical value from its rule expression (condition, like <10 etc). Have you thought about separating expressions and values?
Something like
rule_details table:
t_rule_id rule_type Value
1 > 10
1 < 20
joining that set to the set of information to be checked/validated. With a lot of case statements
case
when rule_type = '>' and value > other_value then true
when rule_type = '>' and value <= other_value then false
...
end as rule_satisfied
you can create a column to validate each number against the criteria set out in the rule details. At that point you can do a logical AND on each group created -> if TRUE then all rules satisfied.

finding range by comparing two tables

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?

How to execute a LIKE query against a DECIMAL (or INTEGER) field?

Is it possible to execute a LIKE statement against a table column that contains DECIMAL types? Or else, what would be the best way to select matching rows given a number in a decimal (or integer) field?
E.g.:
Name Age
... ...
John 25
Mary 76
Jim 45
Erica 34
Anna 56
Bob 55
Executing something like SELECT * FROM table WHERE age LIKE 5 would return:
Name Age
John 25
Jim 45
Anna 56
Bob 55
It is not clear from your question what exactly you are trying to achieve, but based on the example query, the filtering you need to do should be achievable using normal arithmetic operators.
SELECT * FROM table WHERE MOD(age, 10) = 5 -- All records where the age ends in 5
Or:
SELECT * FROM table WHERE MOD(age, 5) = 0 -- All records where age is divisible by 5
Now that you clarified that though you are using a DECIMAL field you are not actually using it as a numeric value (as if you would, the requirement wouldn't exist), the answers given by others are reasonable - convert the field to a text value and use LIKE on it.
Alternatively, change the type of the field to something that is more suitable to the way you are using it.
You can convert your decimal field to varchar and then apply like.
If you create a query
select name from table where age like '%5%'
you could achieve this (at least in mysql and db2)
But if you prefer to match a number you should use something like:
select name from table where age > minimum and age < maximum
Or try to compare against a modulo if you are really interested in querying on the last number.

Access SQL how to make an increment in SELECT query

I Have an SQL query giving me X results, I want the query output to have a coulmn called
count making the query somthing like this:
count id section
1 15 7
2 3 2
3 54 1
4 7 4
How can I make this happen?
So in your example, "count" is the derived sequence number? I don't see what pattern is used to determine the count must be 1 for id=15 and 2 for id=3.
count id section
1 15 7
2 3 2
3 54 1
4 7 4
If id contained unique values, and you order by id you could have this:
count id section
1 3 2
2 7 4
3 15 7
4 54 1
Looks to me like mikeY's DSum approach could work. Or you could use a different approach to a ranking query as Allen Browne described at this page
Edit: You could use DCount instead of DSum. I don't know how the speed would compare between the two, but DCount avoids creating a field in the table simply to store a 1 for each row.
DCount("*","YourTableName","id<=" & [id]) AS counter
Whether you go with DCount or DSum, the counter values can include duplicates if the id values are not unique. If id is a primary key, no worries.
I frankly don't understand what it is you want, but if all you want is a sequence number displayed on your form, you can use a control bound to the form's CurrentRecord property. A control with the ControlSource =CurrentRecord will have an always-accurate "record number" that is in sequence, and that will update when the form's Recordsource changes (which may or may not be desirable).
You can then use that number to navigate around the form, if you like.
But this may not be anything like what you're looking for -- I simply can't tell from the question you've posted and the "clarifications" in comments.
The only trick I have seen is if you have a sequential id field, you can create a new field in which the value for each record is 1. Then you do a running sum of that field.
Add to your query
DSum("[New field with 1 in it]","[Table Name]","[ID field]<=" & [ID Field])
as counterthing
That should produce a sequential count in Access which is what I think you want.
HTH.
(Stolen from Rob Mills here:
http://www.access-programmers.co.uk/forums/showthread.php?p=160386)
Alright, I guess this comes close enough to constitute an answer: the following link specifies two approaches: http://www.techrepublic.com/blog/microsoft-office/an-access-query-that-returns-every-nth-record/
The first approach assumes that you have an ID value and uses DCount (similar to #mikeY's solution).
The second approach assumes you're OK creating a VBA function that will run once for EACH record in the recordset, and will need to be manually reset (with some VBA) every time you want to run the count - because it uses a "static" value to run its counter.
As long as you have reasonable numbers (hundreds, not thousands) or records, the second approach looks like the easiest/most powerful to me.
This function can be called from each record if available from a module.
Example: incrementingCounterTimeFlaged(10,[anyField]) should provide your query rows an int incrementing from 0.
'provides incrementing int values 0 to n
'resets to 0 some seconds after first call
Function incrementingCounterTimeFlaged(resetAfterSeconds As Integer,anyfield as variant) As Integer
Static resetAt As Date
Static i As Integer
'if reset date < now() set the flag and return 0
If DateDiff("s", resetAt, Now()) > 0 Then
resetAt = DateAdd("s", resetAfterSeconds, Now())
i = 0
incrementingCounterTimeFlaged = i
'if reset date > now increments and returns
Else
i = i + 1
incrementingCounterTimeFlaged = i
End If
End Function
autoincrement in SQL
SELECT (Select COUNT(*) FROM table A where A.id<=b.id),B.id,B.Section FROM table AS B ORDER BY B.ID Asc
You can use ROW_NUMBER() which is in SQL Server 2008
SELECT ROW_NUMBER() OVER (ORDER By ID DESC) RowNum,
ID,
Section
FROM myTable
Then RowNum displays sequence of row numbers.