SQL - Create a formatted ouput with placeholder rows - sql

For reasons of our IT department, I am stuck doing this entirely within an SQL query.
Simplified, I have this as an input table:
And I need to create this:
And I am just not sure where to start with this. In my normal C# way of thinking its easy. Column1 is ordered, if the value in Col1 is new, then add a new row to the output and put the contents in column1 in the output. Then, whilst the contents of the input Column1 is unchanged, keep adding the contents of column2 to new rows.
In SQL... nope, I just cannot see the right way to start!

This is a presentation issue that can be easily done in the application or presentation layer. In SQL this can be clunky. The goal of a database is not to render a UI but to store and retrieve data fast and also efficiently, in order to serve as many clients as possible with the same hardware and software resources constraints.
The query that could do this can look like:
with
y as (
select col1, row_number() over(order by col1) as r1
from (select distinct col1 as col1 from t) x
),
z as (
select
t.col1, y.r1, t.col2,
row_number() over(partition by t.col1 order by t.col2) as r2
from t
join y on y.col1 = x.col1
)
select col1, col2
from (
select col1, null as col2, r1, 0 from y
union all
select null, col2, r1, r2 from z
) w
order by r1, r2
As you see, it looks clunky and bloated.

You need a header row for each group which will consist of col1 and null and all the rows of the table with null as col1.
You can do it with UNION ALL and conditional sorting:
select
case when t.col2 is null then t.col1 end col1,
t.col2
from (
select col1, col2 from tablename
union all
select distinct col1, null from tablename
) t
order by
t.col1,
case when t.col2 is null then 1 else 2 end,
t.col2
See the demo (for MySql but it is standard SQL).
Results:
| col1 | col2 |
| ---- | ----- |
| SetA | |
| | BH101 |
| | BH102 |
| | BH103 |
| SetB | |
| | BH201 |
| | BH202 |
| | BH203 |

I agree, formatting should be done outside of SQL, but if you have no choice, here is some SQL Server code that will generate your output
select *
from (
select top 100
case
when col2 is null then ' '+col1
else '' end as firstCol,
IsNull(col2,'') as Col2
from dbo.test t1
group by col1,col2 with rollup
order by col1,col2
) x
where x.firstcol is not null

Related

Select query eliminating unwanted rows

I'm new to SQLite and I am having trouble finding the solution.
I have TABLE1 with columns col1 and col2
col1 col2
-------------
a no
a no
a yes
b no
c yes
c no
d yes
I want no repetitions from col1 but prioritize col2 when having "yes"
I want something like this
col1 col2
-------------
a yes
b no
c yes
d yes
You may try the following:
Approach 1
You may use row_number to retrieve a row number ordered by col2 in descending order that may be used to filter your results eg.
SELECT
col1,
col2
FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY col1
ORDER BY col2 DESC
) rn
FROM
my_table
) t
WHERE rn=1;
col1
col2
a
yes
b
no
c
yes
d
yes
Approach 2
or simply use a group by col1 with the MAX function. The group by will ensure that for each col1 value you will receive the MAX of col2 that is yes if available and no if not.
SELECT
col1,
MAX(col2) as col2
FROM
my_table
GROUP BY
col1;
col1
col2
a
yes
b
no
c
yes
d
yes
View working demo on DB Fiddle
ggordon's answer will work well enough, but just since a window function isn't strictly necessary I figured I'd pass another solution:
select distinct
a.col1,
ifnull(b.col2, 'no') col2
from my_table a
left join (
select distinct
col1,
col2
from my_table
where col2 = 'yes'
) b on a.col1 = b.col1
Output:
| col1 | col2 |
| ---- | ---- |
| a | yes |
| b | no |
| c | yes |
| d | yes |
You will first want to do a distinct select on column one. Then you will want to make a case statement which is essentially a if statement in other languages. The case needs to be if column 1 is yes return it. if it is not yes then return no. It would look something like this
CASE
WHEN condition1 THEN result1
WHEN condition2 THEN result2
WHEN conditionN THEN resultN
ELSE result
END;

sql server : how to get this result USING SQL SERVER QUERY?

i have sql table like:
COL1 | COL2
A | P
A | Q
B | P
B | Q
I want a result like:
COL1 |COL2
A | P
B | Q
This would do it:
select COL1,
case
when COL1 = 'A' then MIN(COL2)
when COL1 = 'B' then MAX(COL2)
end
from #table
group by COL1
But the logic behind it is very limited to your sample result and wouldn't scale well. You need to provide more information. What is the logic behind getting the values for COL2?

How to add a column to a row in a select

Say I have this table
| Col |
-------
| ABC |
| DEF |
What query should I write to obtain this result (not literally this result, but a general way to do that)?
| Col | Col2 |
--------------
| ABC | 0 |
| ABC | 1 |
| DEF | 0 |
| DEF | 1 |
Unless I'm missing something, this should give you the results you're looking for:
Select Col, Col2
From YourTable
Cross Join (Select 0 As Col2 Union Select 1 As Col2) X
Order By Col, Col2
I would guess that you want to pair two columns, for each combination. Your question is vague and not specific to a problem. That's my assumption.
I guess this query could do:
Select Table1.Col1, Table2.Col2 from Table1 LEFT JOIN Table2 on 1=1
This way, you pair up every row from table1 with every row from table2.
Edit, without table2:
Select Table1.Col1, Constructed.Col1 from Table1 LEFT JOIN
(Select 1 as Col1 UNION Select 2 as Col1 UNION
Select 7 as Col1 UNION Select 14 as Col1) Constructed on 1=1
Can you test query, is this what you want?
select * from
(select col1, 0 b from table) table1
union all (select col1, 1 b from table) order by 1;

Group Concat in Redshift

I have a table like this:
| Col1 | Col2 |
|:-----------|------------:|
| 1 | a;b; |
| 1 | b;c; |
| 2 | c;d; |
| 2 | d;e; |
I want the result to be some thing like this.
| Col1 | Col2 |
|:-----------|------------:|
| 1 | a;b;c;|
| 2 | c;d;e;|
Is there some way to write a set function which adds unique values in a column into an array and then displays them. I am using the Redshift Database which mostly uses postgresql with the following difference:
Unsupported PostgreSQL Functions
Have a look at Redshift's listagg() function which is similar to MySQL's group_concat. You would need to split the items first and then use listagg() to give you a list of values. Do take note, though, that, as the documentation states:
LISTAGG does not support DISTINCT expressions
(Edit: As of 11th October 2018, DISTINCT is now supported. See the docs.)
So will have to take care of that yourself. Assuming you have the following table set up:
create table _test (col1 int, col2 varchar(10));
insert into _test values (1, 'a;b;'), (1, 'b;c;'), (2, 'c;d;'), (2, 'd;e;');
Fixed number of items in Col2
Perform as many split_part() operations as there are items in Col2:
select
col1
, listagg(col2, ';') within group (order by col2)
from (
select col1, split_part(col2, ';', 1) as col2 from _test
union select col1, split_part(col2, ';', 2) as col2 from _test
)
group by col1
;
Varying number of items in Col2
You would need a helper here. If there are more rows in the table than items in Col2, a workaround with row_number() could work (but is expensive for large tables):
with _helper as (
select
(row_number() over())::int as part_number
from
_test
),
_values as (
select distinct
col1
, split_part(col2, ';', part_number) as col2
from
_test, _helper
where
length(split_part(col2, ';', part_number)) > 0
)
select
col1
, listagg(col2, ';') within group (order by col2) as col2
from
_values
group by
col1
;

How to retrieve 2nd latest date from a table

I am trying to retrieve second latest date from a table. For example, consider this as my table:
COL1| COL2| COL3
---------------------
A | 1 | 25-JUN-14
B | 1 | 25-JUN-14
C | 1 | 25-JUN-14
A | 1 | 24-JUN-14
B | 1 | 24-JUN-14
C | 1 | 24-JUN-14
A | 1 | 23-JUN-14
B | 1 | 23-JUN-14
C | 1 | 23-JUN-14
I come up with this query which would get the result I want(2nd latest date).
SELECT sub.COL1, sub.COL2, MAX(sub.COL3)
FROM (SELECT t.COL1, t.COL2, t.COL3
FROM test t
GROUP BY t.COL1, t.COL2, t.COL3
HAVING MAX(t.COL3) < (
SELECT MAX(COL3)
FROM test sub
WHERE sub.COL1=t.COL1 AND sub.COL2=t.COL2
GROUP BY COL1, COL2)) sub
GROUP BY sub.COL1, sub.COL2;
As you can see it's big and messy statement with multiple nested sub queries just to get a 2nd latest date. I would love to learn an elegant solution for my problem rather that this mess. Appreciate your help.. :)
PS: I am not allowed to use 'WITH' command.. :(
If I understand correctly, you can do:
select t.*
from (select t.*,
dense_rank() over (order by col3 desc) as seqnum
from test t
) t
where seqnum = 2;
You can try like this:-
SELECT col1, col2, MAX(col3)
FROM TEST
WHERE col3 < (SELECT MAX(col3)
FROM tab1)
GROUP BY col1, col2;
Sql Fiddle Demo