Aligning offset data values sql join - sql

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

Related

Filter rows and select in to another columns in SQL?

I have a table like below.
If(OBJECT_ID('tempdb..#temp') Is Not Null)
Begin
Drop Table #Temp
End
create table #Temp
(
Type int,
Code Varchar(50),
)
Insert Into #Temp
SELECT 1,'1'
UNION
SELECT 1,'2'
UNION
SELECT 1,'3'
UNION
SELECT 2,'4'
UNION
SELECT 2,'5'
UNION
SELECT 2,'6'
select * from #Temp
And would like to get the below result.
Type_1
Code_1
Type_2
Code_2
1
1
2
4
1
2
2
5
1
3
2
6
I have tried with union and inner join, but not getting desired result. Please help.
You can use full outer join and cte as follows:
With cte as
(Select type, code,
Row_number() over (partition by type order by code) as rn
From your_table t)
Select t1.type, t1.code, t2.type, t2.code
From cte t1 full join cte t2
On t1.rn = t2.rn and t1.type =1 and t2.type = 2
Here is a query which will produce the output you expect:
WITH cte AS (
SELECT t.[Type], t.Code
, rn = ROW_NUMBER() OVER (PARTITION BY t.[Type] ORDER BY t.Code)
FROM #Temp t
)
SELECT Type_1 = t1.[Type], Code_1 = t1.Code
, Type_2 = t2.[Type], Code_2 = t2.Code
FROM cte t1
JOIN cte t2 ON t1.rn = t2.rn AND t2.[Type] = 2
AND t1.[Type] = 1
This query is will filter out any Type_1 records which do not have a Type_2 record. This means if there are an uneven number of Type_1 vs Type_2 records, the extra records will get eliminated.
Explanation:
Since there is no obvious way to join the two sets of data, because there is no shared key between them, we need to create one.
So we use this query:
SELECT t.[Type], t.Code
, rn = ROW_NUMBER() OVER (PARTITION BY t.[Type] ORDER BY t.Code)
FROM #Temp t
Which assigns a ROW_NUMBER to every row...It restarts the numbering for every Type value, and it orders the numbering by the Code.
So it will produce:
| Type | Code | rn |
|------|------|----|
| 1 | 1 | 1 |
| 1 | 2 | 2 |
| 1 | 3 | 3 |
| 2 | 4 | 1 |
| 2 | 5 | 2 |
| 2 | 6 | 3 |
Now you can see that we have assigned a key to each row of Type 1's and Type 2's which we can use for the joining process.
In order for us to re-use this output, we can stick it in a CTE and perform a self join (not an actual type of join, it just means we want to join a table to itself).
That's what this query is doing:
SELECT *
FROM cte t1
JOIN cte t2 ON t1.rn = t2.rn AND t2.[Type] = 2
AND t1.[Type] = 1
It's saying, "give me a list of all Type 1 records, and then join all Type 2 records to that using the new ROW_NUMBER we've generated".
Note: All of this works based on the assumption that you always want to join the Type 1's and Type 2's based on the order of their Code.
You can also do this using aggregation:
select max(case when type = 1 then type end) as type_1,
max(case when type = 1 then code end) as code_1,
max(case when type = 2 then type end) as type_2,
max(case when type = 2 then code end) as code_2
from (select type, code,
row_number() over (partition by type order by code) as seqnum
from your_table t
) t
group by seqnum;
It would be interesting to know which is faster -- a join approach or aggregation.
Here is a db<>fiddle.

Convert column value to row value

In SQL Server, I am trying to convert the from table 1 to table 2. From reading other answers from stack overflow, I can do some sort of row_number(). But the problem is I need do some inner join after the conversion because the following script use max() aggregate function, it kind force other fields from other tables to have some sort of aggregate function as well. So I was wondering if there is an alternative approach to solve this problem? Or if there is a way to handle this aggregate function when do join with another table.
select max(case when key = 'ab' then Value end) as ab,
max(case when key = 'cd' then Value end) as cd
from (select t.*,
row_number() over (partition by key order by Value) as seq
from table t
) t
group by seq;
table 1
table 2
You can try with this below script-
SELECT id,
MAX(CASE WHEN name = 'car1' THEN name END) car1,
MAX(CASE WHEN name = 'car2' THEN name END) car2,
MAX(CASE WHEN name = 'car3' THEN name END) car3
FROM your_table
GROUP BY id
You can go for PIVOT feature.
;WITH src as
(
SELECT *
FROM
(
VALUES
(1, 'Car1', 'nissan'),
(1, 'Car2', 'audi'),
(1, 'Car3', 'toyota')
) as t (id, name, value)
)
SELECT *
FROM src
PIVOT
(
max(VALUE) FOR NAME IN ([Car1], [Car2], [Car3])
) as pvt
+----+--------+------+--------+
| id | Car1 | Car2 | Car3 |
+----+--------+------+--------+
| 1 | nissan | audi | toyota |
+----+--------+------+--------+

What would be the best way to write a query to produce a table given the following data?

I have a table that contains the following data:
ADD_Col Data OrderId Output NEW_ADD Col1 Col2
----- ------ ------- -----> ------- -------- -------
AD*A*1 A 96 A 1 2
AD*A*1 B 95 B 1 1
AD*A*1 C 94 C 0.8 1
AD*A*1 D 93 D 5 2
AD*A*2 1 92
AD*A*2 1 91
AD*A*2 0.8 90
AD*A*2 5 89
AD*A*3 2 88
AD*A*3 1 87
AD*A*3 1 86
AD*A*3 2 85
This data is all in the same table and I need to link each letter to each factor. I was thinking of doing a ROW_NUMBER() and joining based on the respective row number and assign my letter the same number either that or DENSERANK. What would be the best way to achieve this? If you can please provide query examples that would be great thanks.
Seems like what you need to do is normalise your data here. Here I use PARSENAME to get the "column Number", and then ROW_NUMBER to number the relevant rows in the groups. Finally I use a Cross tab to Pivot to data:
WITH CTE AS(
SELECT V.[Key],
V.data,
V.[Order],
PARSENAME(REPLACE(V.[Key],'*','.'),1) AS ColNo,
ROW_NUMBER() OVER (PARTITION BY V.[Key] ORDER BY V.[Order] DESC) AS RN
FROM (VALUES('AD*A*1','A',96),
('AD*A*1','B',95),
('AD*A*1','C',94),
('AD*A*1','D',93),
('AD*A*2','1',92),
('AD*A*2','1',91),
('AD*A*2','0.8',90),
('AD*A*2','5',89),
('AD*A*3','2',88),
('AD*A*3','1',87),
('AD*A*3','1',86),
('AD*A*3','2',85))V([Key],[data],[Order]))
SELECT MAX(CASE C.ColNo WHEN '1' THEN C.[data] END) AS New_ADD,
MAX(CASE C.ColNo WHEN '2' THEN C.[data] END) AS Col1,
MAX(CASE C.ColNo WHEN '3' THEN C.[data] END) AS Col2
FROM CTE C
GROUP BY C.RN;
For your sample data this will work:
with cte as (
select *,
row_number() over (partition by [key] order by [OrderId desc]) rn,
dense_rank() over (order by [key]) rk
from tablename
)
select t1.data,
max(case when t2.rk = 2 then t2.data end) col1,
max(case when t2.rk = 3 then t2.data end) col2
from (select * from cte where rk = 1) t1
inner join (select * from cte where rk in (2, 3)) t2
on t2.rn = t1.rn
group by t1.data
See the demo.
Results:
> data | col1 | col2
> :--- | :--- | :---
> A | 1 | 2
> B | 1 | 1
> C | 0.8 | 1
> D | 5 | 2
select t1.Data "Key"
, t2.Data "Col1"
, t3.Data "Col2"
from ((SELECT Data,
row_number() over (order by Key_C) rn
from my_table
where Key_C = 'AD*A*1') t1
left join
(SELECT Data,
row_number() over (order by Key_C) rn
from my_table
where Key_C = 'AD*A*2') t2
on t1.rn = t2.rn
left join
(SELECT Data,
row_number() over (order by Key_C) rn
from my_table
where Key_C = 'AD*A*3') t3
on t2.rn = t3.rn);
Here is the DEMO
DROP TABLE IF EXISTS #RawData
SELECT
[ADD_Col]
,[Data]
,[OrderId]
,REPLACE([ADD_Col], 'AD*A*', '') AS [Level]
,DENSE_RANK() OVER (PARTITION BY [ADD_Col] ORDER BY [OrderId] DESC) AS [Grouping]
INTO
#RawData
FROM
[SourceTable]
SELECT
rd.[Data]
,rdc1.[Data] AS [Col1]
,rdc2.[Data] AS [Col2]
FROM
#RawData AS rd
LEFT OUTER JOIN #RawData AS rdc1
ON rdc1.[Level] = 2
AND rd.[Grouping] = rdc1.[Grouping]
LEFT OUTER JOIN #RawData AS rdc2
ON rdc2.[Level] = 3
AND rd.[Grouping] = rdc2.[Grouping]
WHERE
rd.[Level] = 1

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;

How to make a query to find this?

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