I am not strong with SQL at all, so here it goes:
I have a table with a column containing doubles.
I would like to select all rows that the maximum difference between them is '5'.
How can I do that?
id value
1 4955.54
2 2884.32
3 8485.45
4 4588.54
5 8487.62
RESULT
id value
3 8485.45
5 8487.62
How can I do that in mySQL ?
Many thanks!
This works, although you mean maximum not minimum difference:
SELECT v.id, v.value
FROM Values v
WHERE EXISTS(
SELECT null from Values v2
WHERE v2.id <> v.id and
ABS(v2.value - v.value) BETWEEN 0 AND 5
)
MSDN: EXISTS (Transact-SQL)
MSDN: ABS (Transact-SQL)
MSDN: BETWEEN (Transact-SQL)
select id, value from table t1
inner join table t2 on t1.id <> t2.id
where ABS(t1.value-t2.value)<=5
It's likely to be inefficient if the set of values is large. There is no obvious way to write this query efficiently, but here goes:
select lo.val
, hi.val
from numbers lo
inner join numbers hi
on hi.val - lo.val >= 5
if the val column is indexed, it might help to add another condition like so:
select lo.val
, hi.val
from numbers lo
inner join numbers hi
on hi.val > lo.val
where hi.val - lo.val >= 5
Related
Suppose:
MyTable
--
Amount
1
2
3
4
5
MyTable only has one column, Amount, with 5 rows. They are not necessarily in increasing order.
How can I create a function, which takes a #SUM INT, and returns the TOP N rows that sum to this amount?
So for input 6, I want
Amount
1
2
3
Since 1 + 2 + 3 = 6. 2 + 4 / 1 + 5 won't work since I want TOP N ROWS
For 7/8/9/10, I want
Amount
1
2
3
4
I'm using MS SQL Server 2008 R2, if this matters.
Saying "top N rows" is indeed ambiguous when it comes to relational databases.
I assume that you want to order by "amount" ascending.
I would add a second column (to a table or view) like "sum_up_to_here", and create something like that:
create view mytable_view as
select
mt1.amount,
sum(mt2.amount) as sum_up_to_here
from
mytable mt1
left join mytable mt2 on (mt2.amount < mt1.amount)
group by mt1.amount
or:
create view mytable_view as
select
mt1.amount,
(select sum(amount) from mytable where amount < mt1.amount)
from mytable mt1
and then I would select the final rows:
select amount from mytable_view where sum_up_to_here < (some value)
If you don't bother about performance you may of course run it in one query:
select amount from
(
select
mt1.amount,
sum(mt2.amount) as sum_up_to_here
from
mytable mt1
left join mytable mt2 on (mt2.amount < mt1.amount)
group by mt1.amount
) t where sum_up_to_here < 20
One approach:
select t1.amount
from MyTable t1
left join MyTable t2 on t1.amount > t2.amount
group by t1.amount
having coalesce(sum(t2.amount),0) < 7
SQLFiddle here.
In Sql Server you can use CDEs to make it pretty simple to read.
Here is a CDE I did to sum up totals used in sequence. The CDE is similar to the joins above, and holds the total up to any given index. Outside of the CDE I join it back to the original table so I can select it along with other fields.
;with summrp as (
select m1.idx, sum(m2.QtyReq) as sumUsed
from #mrpe m1
join #mrpe m2 on m2.idx <= m1.idx
group by m1.idx
)
select RefNum, RefLineSuf, QtyReq, ProjectedDate, sumUsed from #mrpe m
join summrp on summrp.idx=m.idx
In SQL Server 2012 you can use this shortcut to get a result like Grzegorz's.
SELECT amount
FROM (
SELECT * ,
SUM(amount) OVER (ORDER BY amount ASC) AS total
from demo
) T
WHERE total <= 6
A fiddle in the hand... http://sqlfiddle.com/#!6/b8506/6
If there are two tables as mentioned below:
Table 1
day acount
1998-03-01 8
1998-03-04 9
1998-03-05 10
1998-03-09 8
Table 2
day bcount
1998-03-02 9
1998-03-03 7
1998-03-05 4
1998-03-06 3
Can a select query return the data in ascending order in the format below?
Result
day acount bcount
1998-03-01 8 0
1998-03-02 0 9
1998-03-03 0 7
1998-03-04 9 0
1998-03-05 10 4
1998-03-06 3 0
1998-03-09 8 0
I would suggest using a FULL OUTER JOIN to join the tables on the day column to get the result:
select coalesce(t1.day, t2.day) "day",
coalesce(t1.acount, 0) acount,
coalesce(t2.bcount, 0) bcount
from table1 t1
full outer join table2 t2
on t1.day = t2.day;
See SQL Fiddle with Demo. The COALESCE function will return the first non-null result, so this can be used to get the day values in the same column and then replace the nulls in the acount and bcount columns.
#bluefeet's query is the way to go. Only adding some syntactical sugar and corrective:
SELECT day
, coalesce(t1.acount, 0) AS acount
, coalesce(t2.bcount, 0) AS bcount
FROM table1 t1
FULL JOIN table2 t2 USING (day)
SQL Fiddle.
If you use the shorter USING clause for the JOIN condition (possible in this case), you also don't need coalesce(t1.day, t2.day), since this is exactly what day without table qualification resolves to after being listed in the USING clause.
While it is ok to skip the key word AS for table aliases, you should never skip it for column aliases - as documented in the manual in a separate paragraph Omitting the AS Key Word:
In FROM items, both the standard and PostgreSQL allow AS to be omitted
before an alias that is an unreserved keyword. But this is impractical
for output column names, because of syntactic ambiguities.
I am looking for a way to find missing numbers within a range. I have a beginning number column and a ending number column in the same table.
I am trying to get the skipped numbers. I can get the next skipped number, but don't know how to get a list of the numbers that were not in the range. I have a numbers table if that would be useful.
Here is my example:
doc_num_begin doc_num_end
------------- -----------
20000007 20000008
20000011 20000015
20000016 20000017
I'd like to get 20000009,20000010. I have searched but not able to find out how to do this using beginning and ending columns.
Thanks
If you have a numbers table, then this is pretty easy:
select n.num
from Numbers n left outer join
RangeTable rt
on n.number between rt.doc_num_begin and doc_num_end
where rt.doc_num_begin is null
This is doing a left outer join from the numbers to the range table, and then keeping the ones that don't match.
Although pretty easy to express, the performance will probably be rather poor due to the non-equijoin. You may also want to put in conditions on the numbers table, so you don't start at 0, 1, . . ., when the ranges start at 20000007. You would do this as:
select n.num
from Numbers n join
(select MIN(doc_num_begin) as MinVal, MAX(doc_num_end) as MaxVal from RangeTable) const
on n.number between const.MinVal and const.MaxVal left outer join
RangeTable rt
on n.number between rt.doc_num_begin and doc_num_end
where rt.doc_num_begin is null
If you just have to find the missing ranges, you could use this query:
SELECT
t1.doc_num_end + 1 as start_missing_range,
MIN(t2.doc_num_begin) - 1 as end_missing_range
FROM
your_table t1 INNER JOIN your_table t2
ON t1.doc_num_end < t2.doc_num_begin
GROUP BY
t1.doc_num_end
HAVING
MIN(t2.doc_num_begin) - t1.doc_num_end > 1
EDIT: And this query could be used to expand a range:
SELECT num+start_missing_range
FROM
(select 0 as num
union all select 1 as num
union all select 2 as num
union all select 3 as num
union all select 4 as num
union all select 5 as num
union all select 6 as num
union all select 7 as num
union all select 8 as num
union all select 9 as num) numbers inner join
(SELECT
t1.doc_num_end + 1 as start_missing_range,
MIN(t2.doc_num_begin) - 1 as end_missing_range
FROM
your_table t1 INNER JOIN your_table t2
ON t1.doc_num_end < t2.doc_num_begin
GROUP BY
t1.doc_num_end
HAVING
MIN(t2.doc_num_begin) - t1.doc_num_end > 1) rg
on end_missing_range-start_missing_range>=numbers.num
(it will work only if a range contains at maximum 10 numbers, it could be easily expanded to some more... of course, there will always be a limit, but at least you don't need a table with all of the numbers)
You can use known sequence number in any table or sample database for this purpose to filter with this id.
Cross Joining this id, would extend the limit you seek.
SELECT i from (select (w2.WorkOrderID-1)+(w1.WorkOrderID-1)*10000 as i
from AdventureWorks.Production.WorkOrder w1
cross join AdventureWorks.Production.WorkOrder w2
where w1.WorkOrderID<10000 and w2.WorkOrderID<10000) as MyNumbers
WHERE i BETWEEN #StartRange and #EndRange
and not exists (SELECT 1 FROM MyTable
WHERE i BETWEEN doc_num_begin doc_num_end)
I have a table that lists the duration of different activities. It looks like
id duration
1 15
2 30
3 30
4 45
5 30
...etc
I want to sum these activities like
for (lastActivity=1 to 5)
SELECT id, SUM(duration) FROM durations
WHERE id<=lastActivity
to produce an output like
id endtime
1 15
2 45
3 75
4 120
5 150
where each row sums the duration of the activities up to its position in the list.
It seems an easy task (and possibly is), but I can't figure out how the sql should look like to produce such an output. I have tried using GROUP BY together with the OVER clause but perhaps there's a simpler way of doing this.
SELECT t.id,
t.duration,
rt.runningTotal
FROM mytable t
CROSS apply (SELECT Sum(duration) AS runningTotal
FROM emp
WHERE id <= t.id) AS rt
ORDER BY t.id
The APPLY operator allows you to invoke a table-valued function for each row returned by an outer table expression of a query. The table-valued function acts as the right input and the outer table expression acts as the left input. The right input is evaluated for each row from the left input and the rows produced are combined for the final output. The list of columns produced by the APPLY operator is the set of columns in the left input followed by the list of columns returned by the right input.
Note : To use APPLY, the database compatibility level must be at least 90. This was introduced in sql server 2005.
you can use running total
check this post
Running total in sqlserver stackoverflow
This will degrade depending on how large your actual table is, but this should do the trick:
Some interesting reading around this can be found here
SELECT 1 as id, 15 as num into #test
UNION ALL SELECT 2, 30
UNION ALL SELECT 3, 30
UNION ALL SELECT 4, 45
UNION ALL SELECT 5, 30
select
t1.id
,MAX(t1.num) as id_num
,SUM(t2.num) as running_total
from #test t1
LEFT OUTER JOIN #test t2 on t2.id <= t1.id
GROUP BY
t1.id
Try this :
select d2.ID,sum(d1.duration)
from durations d1,durations d2
where d1.id<=d2.id
group by d2.id
I have result of two queries like:
Result of query 1
ID Value
1 4
2 0
3 6
4 9
Result of query 2
ID Value
1 6
2 4
3 0
4 1
I want to add values column "Value" and show final result:
Result of Both queries
ID Value
1 10
2 4
3 6
4 10
plz guide me...
select id, sum(value) as value
from (
select id, value from query1
uninon all
select id, value from query2
) x
group by id
Try using a JOIN:
SELECT
T1.ID,
T1.Value + T2.Value AS Value
FROM (...query1...) AS T1
JOIN (...query2...) AS T2
ON T1.Id = T2.Id
You may also need to consider what should happen if there is an Id present in one result but not in the other. The current query will omit it from the results. You may want to investigate OUTER JOIN as an alternative.
A not particularly nice but fairly easy to comprehend way would be:
SELECT ID,SUM(Value) FROM
(
(SELECT IDColumn AS ID,ValueColumn AS Value FROM TableA) t1
OUTER JOIN
(SELECT IDColumn AS ID,ValueColumn AS Value FROM TableB) t2
) a GROUP BY a.ID
It has the benefits of
a) I don't know your actual table structure so you should be able to work out how to get the two 'SELECT's working from your original queries
b) If ID doesn't appear in either table, that's fine