How to retrieve 2nd latest date from a table - sql

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

Related

SQL - Create a formatted ouput with placeholder rows

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

How to get a count of unique rows?

I'm trying to get a count of rows that only differ by one record so I can find out what is "historically" the correct row by determining the most frequently occurring combination. The rows will look something like this:
RowAVal1 | RowAVal2 | RowAVal3 | DiffVal1
RowAVal1 | RowAVal2 | RowAVal3 | DiffVal1
RowAVal1 | RowAVal2 | RowAVal3 | DiffVal2
RowAVal1 | RowAVal2 | RowBVal1 | DiffVal1
For this example, for the RowAVal1 | RowAVal2 | RowAVal3 combination, the rows with DiffVal1 would be the historically correct combination because it appears the most. I need to figure out how to count these rows.
If I understand correctly, you want the most common value of the fourth column for combinations of the first three. This is called the mode in statistics and is easy to calculate with aggregation and window functions:
select t.*
from (select col1, col2, col3, col4, count(*) as cnt,
row_number() over (partition by col1, col2, col3 order by count(*) desc) as seqnum
from t
group by col1, col2, col3, col4
) t
where seqnum = 1;

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;

SQL rank/dense_rank and how to query/calculate with the result

So I have a table where it dense_ranks my rows.
Here is the table:
COL1 | COL2 | COL3 | DENSE_RANK |
a | b | c | 1 |
a | s | r | 1 |
a | w | f | 1 |
b | b | c | 2 |
c | f | r | 3 |
c | q | d | 3 |
So now I want to select any rows where the rank was only represented once, so the 2 is all alone, but not the 1 or 3. I want to select all the rows where this occurs, but how do I do that?
Some ideas:
-COUNT DISTINCT (RANK())
-COUNT RANK()
but neither of those are working, any ideas? please and thank you!
happy hacking
actual code:
SELECT events.event_type AS "event",
DENSE_RANK() OVER (ORDER BY bw_user_event.pad_id) as rank
FROM user_event
WHERE (software_events.software_id = '8' OR software_events.software_id = '14')
AND (software_events.event_type = 'install')
WITH Dense_ranked_table as (
-- Your select query that generates the table with dense ranks
)
SELECT DENSE_RANK
FROM Dense_ranked_table
GROUP BY DENSE_RANK
HAVING COUNT(DENSE_RANK) = 1;
I don't have SQL Server to test this. So please let me know whether this works or not.
I would think you can add a COUNT(*) OVER (PARTITION BY XXXXX) where XXXXX is what you include in your dense rank.
Then wrap this in a Common Table Expression and select where your new Count is = 1.
Something like this fiddler:
http://sqlfiddle.com/#!6/ae774/1
Code included here as well:
CREATE TABLE T
(
COL1 CHAR,
COL2 CHAR,
COL3 CHAR
);
INSERT INTO T
VALUES
('a','b','c'),
('a','s','r'),
('a','w','f'),
('b','b','c'),
('c','f','r'),
('c','q','d');
WITH CTE AS (
SELECT COL1 ,
COL2 ,
COL3,
DENSE_RANK() OVER (ORDER BY COL1) AS DR,
COUNT(*) OVER (PARTITION BY COL1) AS C
FROM dbo.T AS t
)
SELECT COL1, COL2, COL3, DR
FROM CTE
WHERE C = 1
Would return just the
b, b, c, 2
row from your test data.

Rotate one column and Select it comma seperated [duplicate]

This question already has answers here:
Inner Join Two Table, aggregating varchar fields
(2 answers)
Closed 8 years ago.
I have a table like this:
PK | COL1 | COL2
----------------
1 | A | a
2 | B | b
3 | C | c
4 | A | d
5 | A | e
6 | B | f
7 | C | g
8 | C | h
and I want to do an Select that I get the following result
COL1 | COL2
---------------
A | a,d,e
B | b,f
C | c,g,h
As I understand SQL at the moment I don't know how to do this without "programing" something extra e.q. with PL/SQL
But i search for an DBMS independent solution as good as it can be DBMS independent.
This is an Oracle (11.2) solution:
select col1,
listagg(col2, ',') within group (order by col1) as col2
from the_table
group by col1;
As you need this for other DBMS as well, this would be the Postgres solution:
select col1,
string_agg(col2, ',' order by col1) as col2
from the_table
group by col1;
For MySQL this would be:
select col1,
group_concat(col2 ORDER BY col1 SEPARATOR ',') as col2
from the_table
group by col1;
For a SQL Server solution, see Vijaykumar's answer.
try this !!
SELECT [col1],
SUBSTRING(d.col2,1, LEN(d.col2) - 1) col2
FROM
(
SELECT DISTINCT [col1]
FROM table1
) a
CROSS APPLY
(
SELECT [col2] + ', '
FROM table1 AS b
WHERE a.[col1] = b.[col1]
FOR XML PATH('')
) d (col2)