min value from two columns (Oracle SQL) - sql

Searching for a while now, but haven't found a suitable answer to my problem.
Explanation:
ID | Year | Factor1 | Number1 | Number2
1 | 2010 | 213 | 1 | 1
2 | 2010 | 213 | 1 | 2
3 | 2010 | 214 | 2 | 1
4 | 2010 | 214 | 2 | 2
6 | 2010 | 210 | 3 | 1
7 | 2010 | 210 | 3 | 2
8 | 2011 | 250 | 3 | 5
5 | 2012 | 214 | 2 | 4
EDIT:
Forgot Something, corrected that in the above table.
I need 2 combinations: year and factor1 only once, then min(number1) and last min(number2).
For example above I would need IDs 1,3,5,6,8 (sorry they are mixed for better readability of the other values).
Anyone any idea how to realize that?

SELECT id , year, number1, number2
FROM #table A
WHERE number1 IN (SELECT MIN(number1) FROM #table WHERE year = A.year)
OR number2 IN (SELECT MIN(number2) FROM #table WHERE year = A.year)
where #table is your table

Got the code now from another forum, works fine for me:
SELECT
tab.*
FROM
t33 tab
INNER JOIN (
SELECT
m1.y,
m1.factor1,
m1.min_1,
m2.min_2
FROM
(
SELECT
y,
factor1,
MIN(number1) AS min_1
FROM
t33 tab
GROUP BY
y,
factor1
) m1
INNER JOIN (
SELECT
y,
factor1,
number1,
MIN(number2) AS min_2
FROM
t33
GROUP BY
y,
factor1,
number1
) m2
ON m1.y = m2.y
AND m1.factor1 = m2.factor1
AND m1.min_1 = m2.number1
) sel
ON tab.y = sel.y
AND tab.factor1 = sel.factor1
AND tab.number1 = sel.min_1
AND tab.number2 = sel.min_2

OK, so something like this?
delete from mb_test
where ID not in
( select id
from mb_test mbt
,(select year, min(number1) as min1, min(number2) as min2
from mb_test
group by year) mb_mins
where mbt.year = mb_mins.year
and (mbt.number1 = mb_mins.min1 OR mbt.number2 = mb_mins.min2)
)

Related

Calculate how many rows are ahead of position in column when condition is met

How can I calculate how many people are ahead of Jane on Floor 2 (not including those on floor 1)?
+------+---------+----------+
|Index | Name | Floor |
+------+---------+----------+
| 1 | Sally | 1 |
| 2 | Sue | 1 |
| 3 | Fred | 1 |
| 4 | Wally | 2 |
| 5 | Tommy | 2 |
| 6 | Jane | 2 |
| 7 | Bart | 2 |
| 8 | Sam | 3 |
+------+---------+----------+
The expected result is 2 as there are 2 people (Wally & Tommy) ahead of Jane on floor 2.
I've tried using CHARINDEX to find the row number from a temp table that I've generated but that doesn't seem to work:
SELECT CHARINDEX('Jane', Name) as position
INTO #test
FROM tblExample
WHERE Floor = 2
select ROW_NUMBER() over (order by position) from #test
WHERE position = 1
I think a simple row_number() would do the trick
Select Value = RN-1
From (
Select *
,RN = row_number() over (partition by [floor] order by [index])
From YourTable
Where [Floor]=2
) A
Where [Name]='Jane'
You could do:
select count(*)
from t
where t.floor = 2 and
t.id < (select t2.id from t t2 where t2.name = 'Jane' and t2.floor = 2);
With an index on (floor, name, id), I would expect this to be faster than row_number().

Values Disappear when Filtering Correlated Subquery

This question is related to the recent answer I provided here.
Setup
Using MS Access 2007.
Assume I have a table called mytable consisting of three fields:
id Long Integer AutoNumber (PK)
type Text
num Long Integer
With the following sample data:
+----+------+-----+
| id | type | num |
+----+------+-----+
| 1 | A | 10 |
| 2 | A | 20 |
| 3 | A | 30 |
| 4 | B | 40 |
| 5 | B | 50 |
| 6 | B | 60 |
| 7 | C | 70 |
| 8 | C | 80 |
| 9 | C | 90 |
| 10 | D | 100 |
+----+------+-----+
Similar to the linked answer, say I wish to output the three fields, with a running total for each type value, with the value of the running total limited to a maximum of 100, I might use a correlated subquery such as the following:
select q.* from
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
) q
where q.rt < 100
This produces the expected result:
+----+------+-----+----+
| id | type | num | rt |
+----+------+-----+----+
| 1 | A | 10 | 10 |
| 2 | A | 20 | 30 |
| 3 | A | 30 | 60 |
| 4 | B | 40 | 40 |
| 5 | B | 50 | 90 |
| 7 | C | 70 | 70 |
+----+------+-----+----+
Observation
Now assume that I wish to filter the result to show only those values for type like "[AB]".
If I use either of the following queries:
select q.* from
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
where t.type like "[AB]"
) q
where q.rt < 100
select q.* from
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
) q
where q.rt < 100 and q.type like "[AB]"
The results are filtered as expected, but the values in the rt (running total) column disappear:
+----+------+-----+----+
| id | type | num | rt |
+----+------+-----+----+
| 1 | A | 10 | |
| 2 | A | 20 | |
| 3 | A | 30 | |
| 4 | B | 40 | |
| 5 | B | 50 | |
+----+------+-----+----+
Question
Why would the filter cause the values returned by the correlated subquery to disappear?
Thank you for your time reading my question and in advance for any advice you can offer.
Moving type criteria to the aggregate subquery works.
One less tier works but the aggregate subquery has to repeat in WHERE clause:
SELECT mytable.*, (select sum(u.num)
from mytable u where u.type = MyTable.type and u.id <= MyTable.id
) AS rt
FROM mytable
WHERE ((((select sum(u.num)
from mytable u where u.type = MyTable.type and u.id <= MyTable.id
))<100) AND ((mytable.[type]) Like "[AB]"));
An INNER JOIN version:
select MyTable.*, q.* from MyTable INNER JOIN
(
select t.id, t.type, t.num,
(
select sum(u.num)
from mytable u where u.type = t.type and u.id <= t.id
) as rt
from mytable t
) q
ON q.id=MyTable.ID
where q.rt < 100 AND MyTable.Type LIKE "[AB]";

How to roll up based on a few criteria in SQL

I have a data table like this:
QuestionID UserName UserWeightingForQuestion AnswerGivenForQuestion Metric
1 A 1.50 1 ToBeCalculated
1 B 1.00 2 ToBeCalculated
1 C 1.80 3 ToBeCalculated
1 D 1.20 1 ToBeCalculated
1 E 1.40 2 ToBeCalculated
2 A 1.20 2 ToBeCalculated
2 B 1.20 2 ToBeCalculated
2 C 1.10 4 ToBeCalculated
2 D 1.20 5 ToBeCalculated
...
For each question group, I'd like to fill each cell under Metric column with a calculated value defined as shown below:
Metric_For_User_A_For_QuestionID_X = SUM(Weights_With_The_Answer_Similar_To_What_Is_Given_By_User_A_In_QuestionID_Group = X) / DISTINCT(All_WEeights_In_One_QuestionID_Group = X)
Specifically speaking,
Metric_For_User_A_For_QuestionID_1 = SUM(1.50+1.20)/(1.50+1.00+1.80+1.20+1.40)
Metric_For_User_B_For_QuestionID_1 = SUM(1.00+1.40)/(1.50+1.00+1.80+1.20+1.40)
Metric_For_User_C_For_QuestionID_1 = SUM(1.80)/(1.50+1.00+1.80+1.20+1.40)
Metric_For_User_D_For_QuestionID_1 = SUM(1.50+1.20)/(1.50+1.00+1.80+1.20+1.40)
Metric_For_User_E_For_QuestionID_1 = SUM(1.00+1.40)/(1.50+1.00+1.80+1.20+1.40)
For QuestionID group = 2, I'd like to repeat the process as above. For example,
Metric_For_User_A_For_QuestionID_2 = SUM(1.20+1.20)/(1.20+1.10)
I'm fairly new to SQL and I believe the OVER or some sort of aggregation function can be utilized to achieve this(?) If this kind of calculation is possible in SQL, could someone with SQL expertise suggest me a way to achieve what I'm trying to calculate.
The raw table has ~70m rows, and I am using SQL Server. Thank you very much in advance for your suggestions and answers!
You can use the SUM window function to do this.
select t.*,
sum(UserWeightingForQuestion) over(partition by questionID,AnswerGivenForQuestion)
/sum(UserWeightingForQuestion) over(partition by questionID) as metric
from tablename t
sum(UserWeightingForQuestion) over(partition by questionID) gets the sum of all UserWeightingForQuestion per questionID
sum(UserWeightingForQuestion) over(partition by questionID,AnswerGivenForQuestion) sums up the similar UserWeightingForQuestion per questionID
Edit: To sum up the distinct weights for each questionID in the denominator, use
select t.*,
sum(UserWeightingForQuestion) over(partition by questionID,AnswerGivenForQuestion)
/(select sum(distinct UserWeightingForQuestion) from tablename where t.questionID=questionID) as metric
from tablename t
declare #quest table(QuestionID int
, UserName varchar(20)
, UserWeightingForQuestion decimal(10,2)
, AnswerGivenForQuestion int);
insert into #quest values
(1,'A',1.50,1),(1,'B',1.00,2),(1,'C',1.80,3),(1,'D',1.20,1),
(1,'E',1.40,2),(2,'A',1.20,2),(2,'B',1.20,2),(2,'C',1.10,4),(2,'D',1.20,5);
Baicaly you made two partitions, one by QuestionID and AnswerGivenForQuestion, and another by QuestionID.
WITH CALC AS
(
SELECT Q2.QuestionID, Q2.UserName,
SUM(UserWeightingForQuestion) OVER (PARTITION BY QuestionID, AnswerGivenForQuestion) AS Weight,
(SELECT SUM(DISTINCT Q1.UserWeightingForQuestion)
FROM #quest Q1
WHERE Q1.QuestionID = Q2.QuestionID) AS AllWeights
FROM #quest Q2
)
SELECT QuestionID, UserName, Weight, AllWeights,
CAST(Weight / AllWeights AS DECIMAL(18,2)) as Metric
FROM CALC
ORDER BY QuestionID, UserName;
+------------+----------+--------+------------+--------+
| QuestionID | UserName | Weight | AllWeights | Metric |
+------------+----------+--------+------------+--------+
| 1 | A | 2,70 | 6,90 | 0,39 |
| 1 | B | 2,40 | 6,90 | 0,35 |
| 1 | C | 1,80 | 6,90 | 0,26 |
| 1 | D | 2,70 | 6,90 | 0,39 |
| 1 | E | 2,40 | 6,90 | 0,35 |
+------------+----------+--------+------------+--------+
| 2 | A | 2,40 | 2,30 | 1,04 |
| 2 | B | 2,40 | 2,30 | 1,04 |
| 2 | C | 1,10 | 2,30 | 0,48 |
| 2 | D | 1,20 | 2,30 | 0,52 |
+------------+----------+--------+------------+--------+

How to merge two different rows(how to assign different value is zero)

I am trying to use union for merging two output but these rows value are different.I need different rows value are zero.like output(third) table.I was struggle with pass two days please help me.
Select t1.round,
t1.SC,
t1.ST,
t1.OTHERS,
t2.round_up,
t2.SC_up,
t2.ST_up,
t2.OTHERS_up
From
(Select round as round,
Sum (non_slsc_qty) as SC,
Sum (non_slst_qty) as ST,
Sum (non_slot_qty) as OTHERS
FROM vhn_issue
where (date between '2015-08-01' and '2015-08-31')AND
dvn_cd='15' AND phc_cd='012' AND hsc_cd='05' GROUP BY round) t1
,
(Select round as round_up,
Sum (non_slsc_qty) as SC_up,
Sum (non_slst_qty) as ST_up,
Sum (non_slot_qty) as OTHERS_up,
FROM vhn_issue
where (date between '2015-04-01' and '2015-08-31')AND
dvn_cd='15' AND phc_cd='012' AND hsc_cd='05' GROUP BY round) t2
This first table result
+-----------------------------------+------------+--------+--------
| round | SC | ST | OTHERS |
+-----------------------------------+------------+--------+--------
| 1 | 20 | 30 | 50 |
| | | | |
| | | | |
+-----------------------------------+------------+--------+--------+
This is second table result
+-----------------------------------+------------+--------+----------
| round_up | SC_up | ST_up | OTHERS_up |
+-----------------------------------+------------+--------+-----------
| 1 | 21 | 31 | 51 |
| 3 | 10 | 5 | 2 |
| | | | |
+-----------------------------------+------------+--------+--------+---
I need output like this
+------------+--------+----------------------------------------------
| round_up | SC | ST |OTHERS | SC_up | ST_up |OTHERS_up |
+------------+--------+-----------------------------------------------
| 1 | 20 | 30 | 50 | 21 | 31 | 51 |
| | | | | | | |
| 3 | 0 | 0 | 0 | 10 | 5 | 2 |
+------------+--------+--------+---------------------------------------
You can use WITH Queries (Common Table Expressions) to wrap the two selects and use RIGHT JOIN to get the desired output,COALESCE is used to print 0 instead of NULL.
WITH a
AS (
SELECT round AS round
,Sum(non_slsc_qty) AS SC
,Sum(non_slst_qty) AS ST
,Sum(non_slot_qty) AS OTHERS
FROM vhn_issue
WHERE (
DATE BETWEEN '2015-08-01'
AND '2015-08-31'
)
AND dvn_cd = '15'
AND phc_cd = '012'
AND hsc_cd = '05'
GROUP BY round
)
,b
AS (
SELECT round AS round_up
,Sum(non_slsc_qty) AS SC_up
,Sum(non_slst_qty) AS ST_up
,Sum(non_slot_qty) AS OTHERS_up
,
FROM vhn_issue
WHERE (
DATE BETWEEN '2015-04-01'
AND '2015-08-31'
)
AND dvn_cd = '15'
AND phc_cd = '012'
AND hsc_cd = '05'
GROUP BY round
)
SELECT coalesce(b.round_up, 0) round_up
,coalesce(a.sc, 0) sc
,coalesce(a.st, 0) st
,coalesce(a.others, 0) others
,coalesce(b.sc_up, 0) sc_up
,coalesce(b.st_up, 0) st_up
,coalesce(b.others_up, 0) others_up
FROM a
RIGHT JOIN b ON a.round = b.round_up
WITH Results_CTE AS
(
Select t1.round as round_up ,
t1.SC as SC,
t1.ST as ST,
t1.OTHERS as OTHERS,
0 as SC_up,
0 as ST_up,
0 as OTHERS_up
from round t1
union all
t2.round_up as round_up ,
0 as SC,
0 as ST,
0 as OTHERS,
t2.SC_up,
t2.ST_up,
t2.OTHERS_up from round t2
)
select round_up , sum(SC) as SC,sum (ST) as ST, sum(OTHERS) as OTHERS, sum(SC_up) as SC_up, sum(ST_up) as ST_up, sum(OTHERS_up) as OTHERS_ up
from Results_CTE group by round_up

SQL query: select the last record where a value went below a threshold

Given this example data set:
-----------------------------
| item | date | val |
-----------------------------
| apple | 2012-01-11 | 15 |
| apple | 2012-02-12 | 19 |
| apple | 2012-03-13 | 7 |
| apple | 2012-04-14 | 6 |
| orange | 2012-01-11 | 15 |
| orange | 2012-02-12 | 8 |
| orange | 2012-03-13 | 11 |
| orange | 2012-04-14 | 9 |
| peach | 2012-03-13 | 5 |
| peach | 2012-04-14 | 15 |
-----------------------------
I'm looking for the query that for each item,
it will select the first date where the val went below CONST=10 without coming back above afterwards. In this example that would be:
-----------------------------
| item | date | val |
-----------------------------
| apple | 2012-03-13 | 7 |
| orange | 2012-04-14 | 9 |
-----------------------------
Is this even possible without using cursors? I'm looking for this in Sybase.
If this is not possible without cursors, I can process the records in a programming language. In that case however, since in my real use case the full history is very long, I need a "suitable" query that selects just "enough" records for computing the record I am ultimately after: the most recent record where val dipped below CONST without coming back above it.
This returns the result set as detailed.
select tablename.* from tablename
inner join
(
select tablename.item, min(tablename.[date]) as mindate
from tablename
inner join (
select
item,
max([date]) lastoverdate
from tablename
where val>#const
group by item
) lastover
on tablename.item = lastover.item
and tablename.[date]> lastoverdate
group by tablename.item
) below
on tablename.item = below.item
and tablename.date = below.mindate
For MySql:
select t.item, t.date1, t.val
from
(
select min(date) date1, item from tablename t
where not exists (select 1 from tablename where item = t.item
and date1 > t.date1 and val >= 10)
and val < 10
group by item
) a
inner join
#t t
on a.item = t.item and a.date1 = t.date1
For different databases such as MS-sql 2005+:
select item, date, val from
(
select *, row_number() over (partition by item order by date) rn
from tablename t
where not exists (select 1 from #t where item = t.item and
date > t.date and val >= 10)
and val < 10
) a where rn = 1