SQL Server - Create subset table having 2 largest values in row - sql

I have a table (11 columns) having only one row. It contains below records:
ID Data_Type Value1 Value2 Value3 Value4 Value5 Value6 Value7 Value8 Value9
1 A_1 08/03/2020 08/03/2020 08/03/2020 08/02/2021 08/02/2021 08/02/2021 08/09/2022 08/09/2021 08/09/2024
I need a subset table with below records (Only 2 columns having latest date values)
ID Data_Type Value9 Value7
1 A_1 08/09/2024 08/09/2022
Please help.

If possible, I'd first change your data structure and merge all value columns into one as values are a lot easier to compare with each other when they're in the same column (like trying to sort and filter values in an Excel spreadsheet - You would usually transpose the columns into one then sort/filter it).
One way that you can merge all value columns into one column is by using UNION ALL:
SELECT ID, Data_Type, Value1 AS NewValue INTO NewTable FROM Table UNION ALL
SELECT ID, Data_Type, Value2 FROM Table UNION ALL
SELECT ID, Data_Type, Value3 FROM Table UNION ALL
SELECT ID, Data_Type, Value4 FROM Table UNION ALL
SELECT ID, Data_Type, Value5 FROM Table UNION ALL
SELECT ID, Data_Type, Value6 FROM Table UNION ALL
SELECT ID, Data_Type, Value7 FROM Table UNION ALL
SELECT ID, Data_Type, Value8 FROM Table UNION ALL
SELECT ID, Data_Type, Value9 FROM Table
After you've done this and all the new data is in NewTable, you can use the following query to extract the top 2 values:
SELECT TOP 2 *
FROM NewTable
ORDER BY NewValue DESC

You can unpivot the data using APPLY and then use some aggregation logic:
select t.id, t.data_type, v.*
from t cross apply
(select max(case when seqnum = 1 then date end) as date_1,
max(case when seqnum = 1 then value end) as date_1,
max(case when seqnum = 2 then date end) as date_2,
max(case when seqnum = 2 then value end) as date_2
from (select v.*, row_number() over (order by value desc) as seqnum
from (values (t.value1, 'value1'),
(t.value2, 'value2'),
(t.value3, 'value3'),
. . .
) v(date, value)
) v
) v;
Very important note: This adds four columns, not two columns. The name of the column with the maximum and penultimate value is in a separate column. You can only control the names of the columns if you use dynamic SQL.
The above will work fine on one row. But it will also work on multiple rows as well.

Related

TSQL - Rank Values but return Column Names

I have a table of data points that I need ranked by column.
I have about 500k Id's and 25 columns (as of now).
I'd like to make the query dynamic so that any added columns will not require a code change.
For each ID, I want to find column names of the top 3 values.
The results should be:
[Id]
[Rank1]
[Rank2]
[Rank3]
27807745
Value3
Value2
Value8
96448378
Value6
Value5
Value1
etc
My first attempt was to create a joined table of Id's and column names:
[Id]
[Value1]
[Value]
27807745
Value1
NULL
27807745
Value2
NULL
27807745
Value3
NULL
27807745
Value4
NULL
27807745
Value5
NULL
Then run a looped update, then sequence by Id, Value DESC.
This gets me there but is taking over 2 hours to complete.
I looked at PIVOT and UNPIVOT but both want to return the values, not the column names.
Is there any other way to do this?
Here is an option that will dynamically unpivot your data WITHOUT using Dynamic SQL
Example or dbFiddle
Select A.ID
,B.*
From YourTable A
Cross Apply (
Select Rank1 = max(case when Rnk=1 then [Key] end)
,Rank2 = max(case when Rnk=2 then [Key] end)
,Rank3 = max(case when Rnk=3 then [Key] end)
From (
Select [Key]
,Value
,Rnk = row_number() over (order by convert(int,value) desc)
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper ))
Where [Key] Not IN( 'ID','Other','Columns','ToExclude')
) B1
) B

How can I get count of unique values across two columns in a table?

I have a table with column1 and column2 among other columns?
I need a count of unique values across all rows taken from column1 and column2.
Possible query:
select count(*) from
( (select distinct column1 from table1) UNION
(select distinct column2 from table1) );
I also need a count of unique values across all rows as per below :-
"column1" + "-" + "column2"
"column2" + "-" + "column1"
Possible query:
select count(*) from
( (select distinct column1, column2 from table1) UNION
(select distinct column2, column1 from table1) )
I think there is a possible flaw with both queries.
Sample Data :
Column 1 Column 2
Value1 null
null Value1
Value1 Value2
Value2 Value1
Value4 null
Value5 null
Result for Query 1 : 4 (Value1, Value2, Value4, Value5)
Result for Query 2 : 5 (Value1, Value1-Value2, Value2-Value1, Value4, Value5)
Null is to be ignored with hyphen being excluded. Or hyphen can be ignored. Not particular about the hyphen.
Sounds like you need something similar to this
SELECT
COUNT(DISTINCT [a])
FROM
(SELECT Column1 [a]
FROM TableA
UNION ALL
SELECT Column2
FROM TableA
) [x]

How can I count the number of time each distinct value occurs in a column and then store the value pairs in a two dimensional array?

I have a table named abc which has columns c1,c2,c3.
I want to find out number of time each distinct value occurs and store it in a 2 dimensional array.
It should look like :
{
value1, count1,
value2, count2,
value3, count3,
}
select col1,count(col1) from abc group by col1
union all
select col2,count(col2) from abc group by col2
union all
select col3,count(col3) from abc group by col3

SQL move data from rows to cols

Sorry for the bad title - I simply do not know what to call the thing I want to do.
Here it goes:
In MS SQL Server 2008
I have a temp table with 4000+ rows created with the WITH statement looking like this:
ID (varchar) DATE (int)
AB1135000097 | 20151221
AB1135000097 | 20160119
AB1135000097 | 20160219
AB1135001989 | 20120223
AB1135001989 | 20120323
AB1135001989 | 20120423
.
.
.
I want to pair the data in date-ranges based on DATE.
AB1135000097 | 20151221 | 20160119
AB1135000097 | 20160119 | 20160219
AB1135001989 | 20120223 | 20120323
AB1135001989 | 20120323 | 20120423
Does this action have a name ? (I will add tags to the post when I know what I'm asking for)
Assumed schema
I am assuming that your table is like:
CREATE TABLE "TABLE"
(
tag CHAR(1) NOT NULL,
value INTEGER NOT NULL,
PRIMARY KEY(tag, value)
);
I really shouldn't have to guess the schema though.
Possible answer
Superficially, you might be after:
SELECT t1.tag, t1.value, t2.value
FROM "TABLE" AS t1
JOIN "TABLE" AS t2
ON t1.tag = t2.tag AND t2.value = t1.value + 1
ORDER BY t1.tag, t1.value;
This joins the table with itself, combining rows where the tag column values (A, B, ...) are the same, and where the value column in one row is one more than the value column in the other.
On the other hand, if you add a row ('A', 5) to the table and expect it to appear in the output as part of a row ('A', 3, 5), then the query is much harder to write without using OLAP features.
if you are using Oracle database then you can refer following query to solve this question -
with t as
(
SELECT 'A' Col1, 1 Col2
FROM Dual
UNION ALL
SELECT 'A' Col1, 2 Col2
FROM Dual
UNION ALL
SELECT 'A' Col1, 3 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 4 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 5 Col2
FROM Dual
UNION ALL
SELECT 'B' Col1, 6 Col2 FROM Dual
)
SELECT *
FROM (SELECT Col1,
Col2,
Lead(Col1) Over(ORDER BY Col1, Col2) Col3,
Lead(Col2) Over(ORDER BY Col1, Col2) Col4
FROM t --(your table name)
ORDER BY Col1, Col2)
WHERE Col1 = Col3
as I don't have your table name and table structure I have created one temp table in Query itself.
you need to change From t to From with your table name . .. please change col1 and col2 column name also accordingly.
I found a solution to my problem. Inspired by Jonathan Leffler's solution. Thanks a lot!
It is based on adding row-numbers to the table ordered by ID and DATE, and then self-join with ROW+1 to get the next date as a second date column.
with
SCHEDULE as
( -- remove duplicates and NULL entries
select DISTINCT ID, DATE from TABLE1
where DATE IS NOT NULL
),
SCHEDULE_WITH_ROW as
(
select * from (
select DISTINCT ROW_NUMBER()
OVER (ORDER BY ID, DATE) AS
ROW, ID, DATE
from SCHEDULE) AS SCHED
)
select
S1.ID
, S1.DATE
, S2.DATE
from SCHEDULE_WITH_ROW S1
join SCHEDULE_WITH_ROW S2 on S2.ID = S1.ID and S1.ROW + 1 = S2.ROW

SQL JOIN 3 tables, unique rows, calculation across tables

I have 3 tables. They have the same columns. I need to merge them into one table, such that I only have unique rows, the amount columns needs to be computed across the 3 tables.
e.g.
Table 1
Name1, 2, 100.00
Name5, 3, 25.00
Table 2
Name1, 2, 50.00
Table 3
Name1, 2, 60.00
Desired Result:
Name1, 2, 90.00 i.e. calculated as (100-60+50)
Name5, 3, 25.00
Any ideas? I've tried union but that doesn't calculate for me or show unique rows....
In this solution, I simply took the Min value of the second column since you did not specify how it should be combined across the three tables.
Select Z.Name, Min( SomeInt ) As MinSomeInt
, Sum( SomeDecimal ) As TotalSomeDecimal
From (
Select Name, SomeInt, SomeDecimal
From Table1
Union All
Select Name, SomeInt, SomeDecimal
From Table2
Union All
Select Name, SomeInt, SomeDecimal * -1
From Table3
) As Z
Group By Z.Name
Pretty clear question. Use UNION ALL to combine the tables into one queryable thing, then use GROUP BY to combine rows that share a value.
SELECT Col1, SUM(Col2) as Col2
FROM
(
SELECT Col1, Col2
FROM Table1
UNION ALL
SELECT Col1, Col2
FROM Table2
UNION ALL
SELECT Col1, Col2
FROM Table3
) as sub
GROUP BY Col1