How to make a query to find this? - sql

I have a data like this in a table:
column1 column2
a 1
a 2
b 2
b 3
a 4
c 5
I want a output like this:
column1 column2
a 1-2
b 2-3
a 4-0
c 5-0

Try this query:
with vw1 as
(select table1.*,rownum rn from table1),
vw2 as (select col1,col2,rn,rn - col2 dis from vw1),
vw3 as (select col1,min(rn),to_char(min(col2))||' - '||
case when min(col2) = max(col2) then '0' else to_char(max(col2)) end col2 from vw2
group by col1,dis order by min(rn))
select col1,col2 from vw3;
SQL Fiddle

You haven't really given a lot of information.
- Will there always only be 1 or 2 values in column2, for each value in column1?
- Will column2 always be in order?
- etc, etc?
But, for the given data, the following should give the results you have asked for.
SELECT
column1,
MIN(column2) AS first_column2,
CASE WHEN COUNT(*) = 1 THEN 0 ELSE MAX(column2) END AS final_column2
FROM
(
SELECT
ROW_NUMBER() OVER ( ORDER BY column2, column1) AS sequence_main,
ROW_NUMBER() OVER (PARTITION BY column1 ORDER BY column2 ) AS sequence_c1,
*
FROM
your_table
)
AS sequenced_table
GROUP BY
sequence_main - sequence_c1,
column1
ORDER BY
MIN(sequence_main)
Example calculations:
column1 column2 | sequence_main sequence_c1 main - c1 | group
a 1 | 1 1 0 | a1
a 2 | 2 2 0 | a1
b 2 | 3 1 2 | b2
b 3 | 4 2 2 | b2
a 4 | 5 3 2 | a2
c 5 | 6 1 5 | c5

Try this query...
select * from
(
select col1 as column1,case when LEAD(col1 , 1, 0) OVER (ORDER BY col2) = col1
then concat( LEAD(col2 , 1, 0) OVER (ORDER BY col2),'-'||col2)
else (case when lag(col1,1,0) over (ORDER BY col2) <> col1 then
concat(col2,'-'||'0')else '0' end)
end as column2
from table
order by col2
)
where column2<>'0'
;

You should be carefull using something like
select column1, column2, rownum from table1
to create some unique ids for your data. Per definition is the ordering without an order by in your SQL not defined. Therefore it is by coincidence that
select * from table1
returns your rows in the ordering you inserted it in your database. As the data grows you will get exceptions to this ordering. So it is highly recommended to put a primary key column in your data table to preserve this insert ordering. I included the column id for this.
Using this pimped dataset you could get the requested data using this query:
with data_aggr as (
select column1,
case
when lead(column1,1,' ') over (order by id)<>column1
and lag(column1,1,' ') over (order by id)<>column1
then column2 || '-0'
when lead(column1,1,' ') over (order by id)=column1
and lag(column1,1,' ') over (order by id)<>column1
then column2 || '-' || lead(column2,1) over (order by id)
else null
end aggr_col2
from table1)
select column1, aggr_col2 from data_aggr where not aggr_col2 is null
http://sqlfiddle.com/#!4/cd24d/19

Related

With some WHERE clause criteria, have SQL output go to next line

I am trying to write a query where I have some criteria where I pivot the results. However, due to output file constraints I am looking for the output to create a new line after the pivot exceeds X, even if the ID and such is otherwise the same.
What I am trying to do:
|--ID--|-Value-|
| 1 | val1 |
| 1 | val2 |
| 1 | val3 |
| 2 | val1 |
|--ID--|-Col1-|-Col2-|
| 1 | Val1| Val2|
| 1 | Val3| |
| 2 | Val1| |
SELECT *
FROM table
PIVOT(max(value) for field1 in (t1,t2)
as pvt
ORDER BY UNIQUE_ID
This is just a pivot example to pivot this particular column. However the output has a very strict number of column requirement so I'd be looking for any pivot beyond the 5th to "overflow" to the next row while retaining the unique id. I am looking at PIVOT but I dont think it will work here.
Is this even possible within the Snowflake platform or do I need to explore other options?
This requirement is purely presentation matter and in my opinion should not be performed at the database level. With that being said it is possible to achieve it by numbering rows in group and performing modulo division:
Samle data:
CREATE OR REPLACE TABLE tab
AS
SELECT 1 AS id, 'val1' AS value UNION
SELECT 1 AS id, 'val2' AS value UNION
SELECT 1 AS id, 'val3' AS value UNION
SELECT 2 AS id, 'val1' AS value;
Query:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT
id
,MAX(CASE WHEN rn % 2 = 0 THEN value END) AS col1
,MAX(CASE WHEN rn % 2 = 1 THEN value END) AS col2
FROM cte
GROUP BY id, FLOOR(rn / 2)
ORDER BY id, FLOOR(rn / 2);
Intermediate result:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT id,value, rn, FLOOR(rn / 2) AS row_index, rn % 2 AS column_index
FROM cte
ORDER BY ID, rn;
Generalized:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value) - 1 AS rn
FROM tab
)
SELECT
id
,MAX(CASE WHEN rn % N = 0 THEN value END) AS col1
,MAX(CASE WHEN rn % N = 1 THEN value END) AS col2
-- ....
,MAX(CASE WHEN rn % N = N-1 THEN value END) AS colN
FROM cte
GROUP BY id, FLOOR(rn / N)
ORDER BY id, FLOOR(rn / N);

SQLite: Use subquery result in another subquery

I have following table with data
id | COL1
=========
1 | b
2 | z
3 | b
4 | c
5 | b
6 | a
7 | b
8 | c
9 | a
So i know ID of 'z' (ID = 2) in the table and i will call it Z_ID.
I need to retrieve rows between 'a' and 'c' (including 'a' and 'c').
It must be first 'a' that comes after Z_ID.
'c' must come after Z_ID and after 'a' that i found previously.
Result that i am seeking is:
id | COL1
=========
6 | a
7 | b
8 | c
My SELECT looks like this
SELECT *
FROM table
WHERE id >= (
SELECT MIN(ID)
FROM table
WHERE COL1 = 'a' AND ID > 2
)
AND id <= (
SELECT MIN(ID)
FROM table
WHERE COL1 = 'c'AND ID > 2 and ID > (
SELECT MIN(ID)
FROM table
WHERE COL1 = 'a' AND ID > 2
)
)
I am getting the result that i want. But i am concerned about performance because i am using same subquery two times. Is there a way to reuse a result from first subquery?
Maybe there is cleaner way to get the result that i need?
Use a CTE which will return only once the result of the subquery that you use twice:
WITH cte AS (
SELECT MIN(ID) minid
FROM tablename
WHERE COL1 = 'a' AND ID > 2
)
SELECT t.*
FROM tablename t CROSS JOIN cte c
WHERE t.id >= c.minid
AND t.id <= (
SELECT MIN(ID)
FROM tablename
WHERE COL1 = 'c' and ID > c.minid
)
In your 2nd query's WHERE clause:
WHERE COL1 = 'c'AND ID > 2 and ID > (...
the condition AND ID > 2 is not needed because the next condition and ID > (... makes sure that ID will be greater than 2 so I don't use it either in my code.
See the demo.
Results:
| id | COL1 |
| --- | ---- |
| 6 | a |
| 7 | b |
| 8 | c |
You can use window functions for this:
select t.*
from (select t.*,
min(case when id > min_a_id and col1 = 'c' then id end) over () as min_c_id
from (select t.*,
min(case when col1 = 'a' then id end) over () as min_a_id
from (select t.*,
min(case when col1 = 'z' then id end) over () as z_id
from t
) t
where id > z_id
) t
) t
where id >= min_a_id and id < min_c_id;

Aligning offset data values sql join

Currently, I've got two rows of data pivoted using case statement into two columns. Data is not aligning. Within the case statement, is there a way to align? All values in Column A has a corresponding value in Column B.
+----------+----------+
| Column A | Column B |
+----------+----------+
| Null | 0 |
+----------+----------+
| 40 | Null |
+----------+----------+
| Null | 0 |
+----------+----------+
| 50 | Null |
+----------+----------+
Expected Output:
+----------+----------+
| Column A | Column B |
+----------+----------+
| 40 | 0 |
+----------+----------+
| 50 | 0 |
+----------+----------+
SELECT (CASE WHEN t.[column A] = 'Column A' THEN t.value END) AS [Column A],
(CASE WHEN t.[column B] = 'Column B' THEN t.value END) AS [Column B]
FROM t INNER JOIN t1 ON t.ID = t1.ID
WHERE t1.string = '123455'
cross apply should work for this requirement since there's no conditions.
select t.colA, max(t.ColB) from(
select t1.colA, t2.colB from testA t1
cross apply testA t2
where t1.colA is not null) t
group by t.colA
sql fiddle
SQL tables represent unordered sets. In a sense, there is no way to answer your question, although this would work:
select a, 0 as b
from t
where a is not null;
However, I suspect that you want to align values based on the ordering. For that, you need an ordering column. Something like this should work:
select max(a) as a, max(b) as b
from (select t.*, row_number() over (order by <ordering col>) as seqnum
from t
) t
group by floor( (seqnum - 1) / 2 ); -- floor() is not really needed
For this to work, though, you need a column that specifies the ordering.
Try this:
select t2.weight, t1.invalid
from mytable t1
join mytable t2 on t2.sequence = t1.sequence
where t1.weight is null
and t2.invalid is null
I have put sample data and tried the below. It is working fine. You can use Window functions & GROUP BY aggregate to arrive at the result.
CREATE TABLE #test1 (seq int, columna int, columnb int)
INSERT INTO #test1
values (0,99,null), (0,null,0)
select * from #test1
select seq, MAX(case when rnk_columna = 1 then columna else 0 end) as columna, MAX(case when rnk_columnb = 1 then columnb else 0 end) as columna
from
(select seq, columna, columnb, Row_number() over(partition by seq order by columna desc) as rnk_columna, Row_number() over(partition by seq order by columnb desc) as rnk_columnb
from #test1) as t
group by seq

oracle sql - N number of rows for for distinct value i

Suppose I have TableA w/ ID column:
TableA
ID
1
2
3
I'm hoping to get N rows returned for each distinct id in TableA
(example below is for N=3)
EXPECTED OUTPUT
ID SEQ
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
Is this possible w/ a single SQL statement?
Thanks!!
To get only rows with single ID
SELECT * FROM tab_name WHERE col1 = N ORDER BY col2 [DESC]
To Get how many records under each id
SELECT id, count(*) as count FROM tab_name GROUP BY id
To get specific number of rows per specific ID
SELECT *
FROM
(SELECT id, col2, ROW_NUMBER() OVER(PARTITION BY t.id ORDER BY col3) colX
FROM tab_name t) outerT
WHERE
outerT.colX < N + 1

problem with update query

I have a query
UPDATE Table_1 SET Col1='Y' WHERE ROWID IN (
select ROWID from (
SELECT BUS_ID,
row_number() over (partition by BUS_ID order by BUS_ID) dupe_count,
rowid
from Table_1
WHERE col2 <> 1
AND col3 <> 1
order by dbms_random.value
) ft
where ft.dupe_count = 1
AND ROWNUM <= 1000
);
updates only 1000 rows in table Table_1.
But if i write
UPDATE Table_1 SET Col1='Y' WHERE ROWID IN (
select ROWID from (
SELECT BUS_ID,
row_number() over (partition by BUS_ID order by BUS_ID) dupe_count,
rowid
from Table_1
WHERE col2 <> 1
AND col3 <> 1
order by dbms_random.value
) ft
where ft.dupe_count = 1
and Table_1.BUS_ID = ft.BUS_ID
AND ROWNUM <= 1000
);
it updates all rows of the table irrespective of RoWNUM <= 1000 i.e if i add
Table_1.BUS_ID = ft.BUS_ID
then it updates all rows that satisfies col2<> 1 AND col3<> 1 and ft.dupe_count=1.
The table is having following structure:
BUS_ID | col1 | col2 | col3
1 | | 0 | 0
2 | | 0 | 0
1 | | 0 | 0
3 | | 1 | 1.
Any idea why is it happening.Please help.
Niraj,
An ordinary subquery is evaluated for each table. A correlated subquery is evaluated for each row. And you have made the subquery in your second update statement correlated with the line Table_1.BUS_ID = ft.BUS_ID. And if it evaluates for each row, then it will always satisfy the ROWNUM <= 1000 predicate.
Regards,
Rob.