Sql how to have a repeating number until a certain number then starts over again - sql

I have a table where I need to select data from it and add/create a column that counts up to 5 then keeps repeating until there are no more rows selected.
I essentially need an output that looks like the below:
ColumnName
1
2
3
4
5
1
2
3
4
5
... until all rows are returned.

You didn't specify the database engine. In Oracle you could use:
SELECT CASE MOD(ROWNUM, 5)
WHEN 0 THEN 5
ELSE MOD(ROWNUM, 5)
END AS ONE_TO_FIVE,
t.*
FROM YOUR_TABLE t
EDIT
Or you could use NVL and NULLIF:
SELECT NVL(NULLIF(MOD(ROWNUM, 5), 0), 5) AS ONE_TO_FIVE,
t.*
FROM YOUR_TABLE t

It seems like you want row_number() and modulo arithmetic. That would be something like this:
select 1 + ( (row_number() over (order by id) - 1) % 5)
from t;
id is any column in the table, particularly one that you would use for ordering the result set.
Note: Modulo arithmetic varies by databases. Some use mod() instead of %.

If you are utilizing a database engine with ROW_NUMBER function you can utilize that, and then take the modulo 5 of that value:
SELECT (ROW_NUMBER() + 1) % 5 OVER(),
other items
FROM ...
The above example is for SQL Server, as the DB engine is not specified ATM by OP.

Related

Group rows starting with same number into single row MS Access SQl

I have an MS access table containing the following values
ID Value
1.1 5
1.2 5
1.3 2
2.1 3
2.2 1
2.3 9
Is there a way to get the sum of the values start start with the same ID (e.g. summ all values that start with 1 or 2)
You can extract the part to the left of the decimal:
select left(id, instr(id, '.') - 1), sum(value)
from t
group by left(id, instr(id, '.') - 1)
Assuming that ids are numbers (as they look like), you can use int() to extract their integer part, and aggregate by that:
select int(id) id, sum(value) sum_value from mytabel group by int(id)

Select Table records in given order - Oracle

I have table lets say - Students, with 5 records and id(s) are 1 to 5, now i want to select the records - in a way that result should come like given sorting order of id column
id column should be resulted - 5,2,1,3,4 ( order may vary each time)
is there any other way to do this in oracle sql?
In mysql we have FIELD() clause to do this. I want to achieve this in oracle.
A quick and dirty way uses instr():
order by instr(',5,2,1,3,4,', ',' || id ',')
To handle values not in the string, you can convert them to NULL and sort those accordingly:
order by nullif(instr(',5,2,1,3,4,', ',' || id ','), 0) nulls last
Oracle uses heap-oraganized table, which means by default rows are stored in no particular order. However, while selecting the rows, you could mention ORDER BY clause explicitly to return the result set in ASC(default)/DESC order.
You could use DECODE/CASE to customize the sorting, for example:
with data as(
select level num from dual connect by level <=5
)
select num
from data
order by case
when num = 5 then 1
when num = 2 then 2
when num = 1 then 3
when num = 3 then 4
when num = 4 then 5
end;
NUM
----------
5
2
1
3
4

SQL group table by "leading rows" without pl/sql

I have this table (short example) with two columns
1 a
2 a
3 a3
4 a
5 a
6 a6
7 a
8 a8
9 a
and I would like to group/partition them into groups separated by those leading "a", ideally to add another column like this, so I can address those groups easily.
1 a 0
2 a 0
3 a3 3
4 a 3
5 a 3
6 a6 6
7 a 6
8 a8 8
9 a 8
problem is that setup of the table is dynamic so I can't use staticly lag or lead functions, any ideas how to do this without pl/sql in postgres version 9.5
Assuming the leading part is a single character. Hence the expression right(data, -1) works to extract the group name. Adapt to your actual prefix.
The solution uses two window functions, which can't be nested. So we need a subquery or a CTE.
SELECT id, data
, COALESCE(first_value(grp) OVER (PARTITION BY grp_nr ORDER BY id), '0') AS grp
FROM (
SELECT *, NULLIF(right(data, -1), '') AS grp
, count(NULLIF(right(data, -1), '')) OVER (ORDER BY id) AS grp_nr
FROM tbl
) sub;
Produces your desired result exactly.
NULLIF(right(data, -1), '') to get the effective group name or NULL if none.
count() only counts non-null values, so we get a higher count for every new group in the subquery.
In the outer query, we take the first grp value per grp_nr as group name and default to '0' with COALESCE for the first group without name (which has a NULL as group name so far).
We could use min() or max() as outer window function as well, since there is only one non-null value per partition anyway. first_value() is probably cheapest since the rows are sorted already.
Note the group name grp is data type text. You may want to cast to integer, if those are clean (and reliably) integer numbers.
This can be achieved by setting rows containing a to a specific value and all the other rows to a different value. Then use a cumulative sum to get the desired number for the rows. The group number is set to the next number when a new value in the val column is encountered and all the proceeding rows with a will have the same group number as the one before and this continues.
I assume that you would need a distinct number for each group and the number doesn't matter.
select id, val, sum(ex) over(order by id) cm_sum
from (select t.*
,case when val = 'a' then 0 else 1 end ex
from t) x
The result for the query above with the data in question, would be
id val cm_sum
--------------
1 a 0
2 a 0
3 a3 1
4 a 1
5 a 1
6 a6 2
7 a 2
8 a8 3
9 a 3
With the given data, you can use a cumulative max:
select . . .,
coalesce(max(substr(col2, 2)) over (order by col1), 0)
If you don't strictly want the maximum, then it gets a bit more difficult. The ANSI solution is to use the IGNORE NULLs option on LAG(). However, Postgres does not (yet) support that. An alternative is:
select . . ., coalesce(substr(reft.col2, 2), 0)
from (select . . .,
max(case when col2 like 'a_%' then col1 end) over (order by col1) as ref_col1
from t
) tt join
t reft
on tt.ref_col1 = reft.col1
You can also try this :
with mytable as (select split_part(t,' ',1)::integer id,split_part(t,' ',2) myvalue
from (select unnest(string_to_array($$1 a;2 a;3 a3;4 a;5 a;6 a6;7 a;8 a8;9 a$$,
';'))t) a)
select id,myvalue,myresult from mytable join (
select COALESCE(NULLIF(substr(myvalue,2),''),'0') myresult,idmin id_down
,COALESCE(lead(idmin) over (order by myvalue),999999999999) id_up
from (
select myvalue,min(id) idmin from mytable group by 1
) a) b
on id between id_down and id_up-1

SQL: create sequential list of numbers from various starting points

I'm stuck on this SQL problem.
I have a column that is a list of starting points (prevdoc), and anther column that lists how many sequential numbers I need after the starting point (exdiff).
For example, here are the first several rows:
prevdoc | exdiff
----------------
1 | 3
21 | 2
126 | 2
So I need an output to look something like:
2
3
4
22
23
127
128
I'm lost as to where even to start. Can anyone advise me on the SQL code for this solution?
Thanks!
;with a as
(
select prevdoc + 1 col, exdiff
from <table> where exdiff > 0
union all
select col + 1, exdiff - 1
from a
where exdiff > 1
)
select col
If your exdiff is going to be a small number, you can make up a virtual table of numbers using SELECT..UNION ALL as shown here and join to it:
select prevdoc+number
from doc
join (select 1 number union all
select 2 union all
select 3 union all
select 4 union all
select 5) x on x.number <= doc.exdiff
order by 1;
I have provided for 5 but you can expand as required. You haven't specified your DBMS, but in each one there will be a source of sequential numbers, for example in SQL Server, you could use:
select prevdoc+number
from doc
join master..spt_values v on
v.number <= doc.exdiff and
v.number >= 1 and
v.type = 'p'
order by 1;
The master..spt_values table contains numbers between 0-2047 (when filtered by type='p').
If the numbers are not too large, then you can use the following trick in most databases:
select t.exdiff + seqnum
from t join
(select row_number() over (order by column_name) as seqnum
from INFORMATION_SCHEMA.columns
) nums
on t.exdiff <= seqnum
The use of INFORMATION_SCHEMA columns in the subquery is arbitrary. The only purpose is to generate a sequence of numbers at least as long as the maximum exdiff number.
This approach will work in any database that supports the ranking functions. Most databases have a database-specific way of generating a sequence (such as recursie CTEs in SQL Server and CONNECT BY in Oracle).

Generating Random Number In Each Row In Oracle Query

I want to select all rows of a table followed by a random number between 1 to 9:
select t.*, (select dbms_random.value(1,9) num from dual) as RandomNumber
from myTable t
But the random number is the same from row to row, only different from each run of the query. How do I make the number different from row to row in the same execution?
Something like?
select t.*, round(dbms_random.value() * 8) + 1 from foo t;
Edit:
David has pointed out this gives uneven distribution for 1 and 9.
As he points out, the following gives a better distribution:
select t.*, floor(dbms_random.value(1, 10)) from foo t;
At first I thought that this would work:
select DBMS_Random.Value(1,9) output
from ...
However, this does not generate an even distribution of output values:
select output,
count(*)
from (
select round(dbms_random.value(1,9)) output
from dual
connect by level <= 1000000)
group by output
order by 1
1 62423
2 125302
3 125038
4 125207
5 124892
6 124235
7 124832
8 125514
9 62557
The reasons are pretty obvious I think.
I'd suggest using something like:
floor(dbms_random.value(1,10))
Hence:
select output,
count(*)
from (
select floor(dbms_random.value(1,10)) output
from dual
connect by level <= 1000000)
group by output
order by 1
1 111038
2 110912
3 111155
4 111125
5 111084
6 111328
7 110873
8 111532
9 110953
you don’t need a select … from dual, just write:
SELECT t.*, dbms_random.value(1,9) RandomNumber
FROM myTable t
If you just use round then the two end numbers (1 and 9) will occur less frequently, to get an even distribution of integers between 1 and 9 then:
SELECT MOD(Round(DBMS_RANDOM.Value(1, 99)), 9) + 1 FROM DUAL