Get a certain number of contiguous rows - sql

I've got a school project, kind of huge, and only a few days left, so here is one problem I'm stuck on, hope you can help.
I've got this table:
places(id, otherInfo)
Simple enough, well I need to make a SQL query or PL/SQL function to retrieve a certain number of contiguous rows. For instance if I call the function using getContiguousPlaces(3); On the table which has the rows:
ID
1
4
5
6
18
19
I want to get rows with ID 4, 5 and 6.
How could I do that?

SELECT p.id, p.prev_id1, p.prev_id2
FROM (
SELECT id, LAG(id, 1) OVER(ORDER BY id) prev_id1, LAG(id, 2) OVER(ORDER BY id) prev_id2
FROM places
) p
WHERE p.prev_id1 = id-1
AND p.prev_id2 = id-2
here you go: http://sqlfiddle.com/#!4/a20e1/1
But I guess you could retrieve the datas differently if you wished.
edit: this case if for the parameter "3". But if you want to adapt it with a number "n", you either have to use it within a dynamic query, or maybe use the partition by clause. I'm going to have a look at this one now...

Try this:
WITH t AS
(SELECT p.*, LEVEL l,
CONNECT_BY_ROOT id AS cbr
FROM places p CONNECT BY
PRIOR id = id-1)
SELECT *
FROM t
WHERE cbr IN
(SELECT cbr
FROM t
WHERE l = 3 )
and l <= 3
ORDER BY cbr,
id
The constant 3 should be a parameter
Here is a fiddle

Related

fetch aggregate value along with data

I have a table with the following fields
ID,Content,QuestionMarks,TypeofQuestion
350, What is the symbol used to represent Bromine?,2,MCQ
758,What is the symbol used to represent Bromine? ,2,MCQ
2425,What is the symbol used to represent Bromine?,3,Essay
2080,A quadrilateral has four sides, four angles ,1,MCQ
2614,A circular cone has a curved surface area of ,2,MCQ
2520,Two triangles have sides 5 cm, 11 cm, 2 cm . ,2,MCQ
2196,Life supporting process mediated by water? ,2,Essay
I would like to get random questions where total marks is an input number.
For example if I say 25, the result should be all the random questions whose Sum(QuestionMarks) is 25(+/-1)
Is this really possible using a SQL
select content,id,questionmarks,sum(questionmarks) from quiz_question
group by content,id,questionmarks;
Expected Input 25
Expected Result (Sum of Question Marks =25)
Update:
How do I ensure I get atleast 2 Essay Type Questions (this is just an example) I would extend this for other conditions. Thank you for all the help
S-Man's cumulative sum is the right approach. For your logic, though, I think you want to get up to the first row that is 24 or more. That logic is:
where total - questionmark < 24
If you have enough questions, then you could get exactly 25 using:
with q25 as (
select *
from (select t.*,
sum(questionmark) over (order by random()) as running_questionmark
from t
) t
where running_questionmark < 25
)
select q.ID, q.Content, q.QuestionMarks, q.TypeofQuestion
from q25 q
union all
(select t.ID, t.Content, t.QuestionMarks, t.TypeofQuestion
from t cross join
(select sum(questionmark) as questionmark_25 from q25) x
where not exists (select 1 from q25 where q25.id = t.id)
order by abs(questionmark - (25 - questionmark_25))
limit 1
)
This selects questions up to 25 but not at 25. It then tries to find one more to make the total 25.
Supposing, questionmark is of type integer. Then you want to get some records in random order whose questionmark sum is not more than 25:
You can use the consecutive SUM() window function. The order is random. The consecutive SUM() adds every current value to the previous sum. So, you could filter where SUM() <= <your value>:
demo:db<>fiddle
SELECT
*
FROM (
SELECT
*,
SUM(questionmark) OVER (ORDER BY random()) as total
FROM
t
)s
WHERE total <= 25
Note:
This returns a records list with no more than 25, but as close as possible to it with an random order.
To find an exact match of your value is some sort of combinatorical problem which shouldn't be solved in a database. Especially when there's a random factor. What if your current SUM is 22 and the next randomly chosen value is 4. Would you retry maybe until infinity to randomly find a value = 3? Or are you trying to remove an already counted record with value = 1?

where clause with = sign matches multiple records while expected just one record

I have a simple inline view that contains 2 columns.
-----------------
rn | val
-----------------
0 | A
... | ...
25 | Z
I am trying to select a val by matching the rn randomly by using the dbms_random.value() method as in
with d (rn, val) as
(
select level-1, chr(64+level) from dual connect by level <= 26
)
select * from d
where rn = floor(dbms_random.value()*25)
;
My expectation is it should return one row only without failing.
But now and then I get multiple rows returned or no rows at all.
on the other hand,
>>select floor(dbms_random.value()*25) from dual connect by level <1000
returns a whole number for each row and I failed to see any abnormality.
What am I missing here?
The problem is that the random value is recalculated for each row. So, you might get two random values that match the value -- or go through all the values and never get a hit.
One way to get around this is:
select d.*
from (select d.*
from d
order by dbms_random.value()
) d
where rownum = 1;
There are more efficient ways to calculate a random number, but this is intended to be a simple modification to your existing query.
You also might want to ask another question. This question starts with a description of a table that is not used, and then the question is about a query that doesn't use the table. Ask another question, describing the table and the real problem you are having -- along with sample data and desired results.

Get MAX() on repeating IDs

This is how my query results look like currently. How can I get the MAX() value for each unique id ?
IE,
for 5267139 is 8.
for 5267145 is 4
5267136 5
5267137 8
5267137 2
5267139 8
5267139 5
5267139 3
5267141 4
5267141 3
5267145 4
5267145 3
5267146 1
5267147 2
5267152 3
5267153 3
5267155 8
SELECT DISTINCT st.ScoreID, st.ScoreTrackingTypeID
FROM ScoreTrackingType stt
LEFT JOIN ScoreTracking st
ON stt.ScoreTrackingTypeID = st.ScoreTrackingTypeID
ORDER BY st.ScoreID, st.ScoreTrackingTypeID DESC
GROUP BY will partition your table into separate blocks based on the column(s) you specify. You can then apply an aggregate function (MAX in this case) against each of the blocks -- this behavior applies by default with the below syntax:
SELECT First_column, MAX(Second_column) AS Max_second_column
FROM Table
GROUP BY First_column
EDIT: Based on the query above, it looks like you don't really need the ScoreTrackingType table at all, but leaving it in place, you could use:
SELECT st.ScoreID, MAX(st.ScoreTrackingTypeID) AS ScoreTrackingTypeID
FROM ScoreTrackingType stt
LEFT JOIN ScoreTracking st ON stt.ScoreTrackingTypeID = st.ScoreTrackingTypeID
GROUP BY st.ScoreID
ORDER BY st.ScoreID
The GROUP BY will obviate the need for DISTINCT, MAX will give you the value you are looking for, and the ORDER BY will still apply, but since there will only be a single ScoreTrackingTypeID value for each ScoreID you can pull it out of the ordering.

How to write an SQL query to have alternating pattern between rows (like -1 and 1)?

I cannot get an alternating pattern of 1 an -1 with my database.
This explains what I am trying to do.
ID Purpose Date Val
1 Derp 4/1/1969 1
1 Derp 4/1/1969 -1
2 Derp 4/2/2011 1
2 Derp 4/2/2011 -1
From a database that is something like
ID Purpose Date
1 Derp 4/1/1969
1 Herp 4/1/1911
2 Woot 4/2/1311
2 Wall 4/2/211
Here is my attempt:
SELECT
ID
,Purpose
,Date
,Val as 1
FROM (
SELECT FIRST(Purpose)
FROM DerpTable WHERE Purpose LIKE '%DERP%'
GROUP BY ID, DATE) as HerpTable, DerpTable
WHERE HerpTable.ID = DerpTable.ID AND DerpTable.ID = HerpTable.ID
This query does not work for me because my mssm does not recognize 'FIRST' or 'FIRST_VALUE' as built in functions. Thus, I have no way of numbering the first incident of derp and giving it a value.
Problems:
I am using sql2012 and thus cannot use First.
I tried using last_value and first_value as seen here but get errors indicating that function is not found
A bunch of sql queries. I've been staring at the MSDN T-SQL help pages
This is me right now.
What I need is a fresh perspective and assistance. Am I making this too hard?
Use a subquery along with ROW_NUMBER and the modulo operator:
select
ID,
Purpose,
Date,
case when rownum % 2 = 0 then 1 else -1 end as Val
from (
SELECT
ID
,Purpose
,Date
ROW_NUMBER() over (order by ID) as rownum
FROM (
SELECT
ID,
Purpose,
Date
FROM DerpTable WHERE Purpose LIKE '%DERP%'
GROUP BY ID, DATE) as HerpTable, DerpTable
WHERE HerpTable.ID = DerpTable.ID AND DerpTable.ID = HerpTable.ID
) [t1]
ROW_NUMBER will assign a value to each row, in this case it's an incrementing value. Using the modulus with 2 allows us to check if it's even or odd and assign 1 or -1.
Note: I don't know if this query will run since I don't know the architecture of your database, but the idea should get you there.
You can use first_value() in SQL Server 2012. I'm not sure what the WHERE condition is in your query, but the following should return your desired results:
SELECT ID,
FIRST_VALUE(Purpose) OVER (PARTITION BY ID ORDER BY DATE) as Purpose,
DATE,
2 * ROW_NUMBER() OVER (PARTITION BY ID ORDER BY DATE) - 1
FROM DERPTABLE
Why not add an incremental column, update the table using modulo to determine if it's even or odd, then drop the column?

please help me in building a query for the below mentioned table in sql

i have a table name conversion and i have these below mentioned columns in it i want to multiply Length\width row elements l*w of 'dimension' values and display them in another new table
Please let me know if anything changes for the same logic in ms access
probably it is simple but i dont know exact query to solve the problem waiting for your solutions
ID area length/width dimensions **new column(L*W) here**
1 1 l 3 3*5=15
2 1 w 5
3 2 l 4
4 2 w 8
5 3 l 6
6 3 w 10
7 4 l 12
8 4 w 13
9 4 W 10
waiting for your reply
You could query the table twice: once for lengths and once for widths and then join by area and multiply the values:
select length.area, length.dimension * width.dimension
from
(select area, dimension from conversion where lenwidth = 'l') length
inner join
(select area, dimension from conversion where lenwidth = 'w') width
on length.area = width.area;
Two remarks:
I suppose that it is a typo that you have two width entries for area 4? Otherwise you would have to decide which value to take in above select statement.
It would not be a good idea to keep the old table and have a new table holding the results. What if you change a value? You would have to remember to change the result accordingly every time. So either ditch the old table or use a view instead of a new table.
Try this
select *,
dimensions*(lead(dimensions) over(order by id)) product
from table1;
Or if you want for the set of area then
select *,
case when length_width='l' and (lead(length_width) over(order by id))='w'
then dimensions*(lead(dimensions) over(order by id))
else 0
end as product
from table1;
fiddle