SQL Server, joining all values in column with some values of another - sql

I'm pretty bad at explaining, so I'll try and let my examples do most of the talking. Let's say I have a table like so:
dbo.ExampleTable
===================================
ID Year Data1 Data2
====== ======== ========= =========
12 2016 FOO BAR
13 2016 FOO MAN
14 2016 SAW BAR
20 2017 FOO BAR
21 2017 FOO MAN
27 2017 SAW BAR
29 2017 CHU CAR
44 9999 FOO BAR
48 9999 FOO MAN
51 9999 SAW BAR
52 9999 CHU CAR
Some notes:
ID is unique
(Year, Data1, Data2) is unique
The only values in the Year column will be 2016, 2017 or 9999
I want to create a table from that data that looks like this:
ID_9999 ID_2016 ID_2017
=========== =========== ===========
44 12 20
48 13 21
51 14 27
52 NULL 29
So essentially, for every unique pairing of Data1 and Data2 where Year=9999, I want to create a row which contains the ID of that pairing where Year=9999, as well as the ID for the pairings where Year=2016 and also Year=2017. Additionally, if either 2016 or 2017 do not containing that Data pairing, I want their value as NULL.
This is the query I've got so far:
SELECT tbl9999.ID ID_9999,
tbl2016.ID ID_2016,
tbl2017.ID ID_2017
FROM dbo.ExampleTable tbl9999
LEFT JOIN dbo.ExampleTable tbl2016
ON tbl9999.Data1 = tbl2016.Data1
AND tbl9999.Data2 = tbl2016.Data2
LEFT JOIN dbo.ExampleTable tbl2017
ON tbl9999.Data1 = tbl2017.Data1
AND tbl9999.Data2 = tbl2017.Data2
WHERE tbl9999.Year=9999
AND tbl2016.Year=2016
AND tbl2017.Year=2017
This seems to work mostly fine, however it will generate a table like this:
ID_9999 ID_2016 ID_2017
=========== =========== ===========
44 12 20
48 13 21
51 14 27
*Notice that it's missing the row with the null value in my example above. Is there any way to change my query to include that null value such that I have it in my example?
Please let me know if I'm missing any information or need anything clarified. Thanks in advance!
EDIT:
I was able to find an answer on my own! This is the code I used to achieve my desired result:
SELECT [9999] [ID_9999],
[2016] [ID_2016],
[2017] [ID_2017]
FROM dbo.ExampleTable
PIVOT (MAX([ID]) FOR [Year] IN ([2016],[2017],[9999])) [x]
ORDER BY ID_9999

You can do this in multiple ways. Conditional aggregation seems simple enough:
select max(case when year = 2016 then id end) as id_2016,
max(case when year = 2017 then id end) as id_2017,
max(case when year = 9999 then id end) as id_9999
from (select t.*, row_number() over (partition by year order by id) as seqnum
from dbo.ExampleTable t
) t
group by seqnum
order by seqnum;

Related

How to grouping with distinct or group max sql

i have date like this Data
id name period difference
6172 A 6 10
6172 A 3 10
10099 AB 12 24
10099 AB 6 24
10099 AB 3 24
10052 ABC 12 26
10052 ABC 6 26
10052 ABC 3 26
9014 ABCD 12 21
9014 ABCD 6 21
9014 ABCD 3 21
how to get result like this
id name period difference
6172 A 6 10
10099 AB 12 24
10052 ABC 12 26
9014 ABCD 12 4
i try with distinct on (id), but the result like this
id name period difference
6172 A 6 10
10099 AB 6 24
10052 ABC 6 26
9014 ABCD 6 4
The query you want looks something like:
SELECT DISTINCT ON (id) *
FROM Data
ORDER BY id, period DESC;
Demo
This is probably the most efficient way to write your query on Postgres. Note that DISTINCT ON syntax does not support more than one column in the ON clause. The above logic happens to work here assuming that id would uniquely identify each group (that is, that id would always be unique). If not, then we might have to resort to using ROW_NUMBER with a partition over id and name.
using max()
select id, name, t2.period, difference from tableA t1
inner join
(select id, max(period) as period from tableA
group by id) t2 on t2.id = t1.id
using distinct()
select distinct id, name, t2.period, difference from tableA
it seems you need just max()
select id,name,max(period),max(difference)
from table group by id,name
Though i have not found difference=4 in your sample data but you used that on output,so i guessed its your typo
Use max()
select id, name, max(period), difference from tablename
group by id, name,difference
You can try my code:
SELECT
id, name, max(period), difference
FROM
data_table
group by id, name,difference
order by name
This is a demo link http://sqlfiddle.com/#!17/9ab8d/2

Update a column in the same query as it is to be made in?

In SQL Server, I am trying to create a temp table then join based on the unique index key criteria set forth, organizing them based on their system numbers, it looks a little something like this:
SELECT DISTINCT
t1.[SysNr] AS [oldSysNr], t1.[SysNr] AS [newSysNr],
t1.[Year], t1.[WorkTm], t1.[AMSTC],
t1.[AJCC], t1.[ShifNum], t1.[FMN]
INTO
#TEMPTABLE
FROM
AppData.SCHED AS t1
JOIN
AppData.SCHED AS tS ON t1.ShifNum = tS.ShifNum
AND t1.WorkTM = tS.WorkTM
AND t1.FMN = tS.FMN
WHERE
t1.Year = 2019;
This will essentially give me the old records needed by the oldSysNr column as the year is the param I am looking at. However, I need to update the newSysNr column in the same query, no separate query to update. Is there any way that this can be achieved? I only know of doing update as a separate query.
Here is the data that is produced from the query:
oldSysNr NewSysNr Year WorkTm AMSTC AJJC SHIFNUM FMN
-----------------------------------------------------------
24 24 2019 3 WVB GH 9 YUBMS
25 25 2019 3 BMS MJ 8 YUBMS
26 26 2019 3 BMS PP 8 MJUNL
27 27 2019 4 ZMG MJ 5 MJUNL
28 28 2019 5 BR OP 3 COLHP
Here are the results that are desired:
oldSysNr NewSysNr Year WorkTm AMSTC AJJC SHIFNUM FMN
-----------------------------------------------------------
24 57 2019 3 WVB GH 9 YUBMS
25 58 2019 3 BMS MJ 8 YUBMS
26 59 2019 3 BMS PP 8 MJUNL
27 60 2019 4 ZMG MJ 5 MJUNL
28 61 2019 5 BR OP 3 COLHP
As mentioned, the param it is looking at is the year, and given that, the entire idea is that it would update NewSysNr provided that it was given the same join statement but it would be another year like 2020. Help in any given way on this issue is greatly appreciated.
You should be able to nest a subquery in the FROM clause. In other words, instead of selecting columns x,y,z from the table itself, you can replace the table name with a query which uses an updated WHERE clause.
So in this case it would look something like this: (Note: you may need to adjust the aliasing in the subquery...I'm just lazy. Also, you don't need to select all the columns in the subquery. The query in parenthesis is the subquery)
SELECT DISTINCT
tS.[SysNr] AS [newSysNr], t1.[oldSysNr]
t1.[Year], t1.[WorkTm], t1.[AMSTC],
t1.[AJCC], t1.[ShifNum], t1.[FMN]
INTO
#TEMPTABLE
FROM
(SELECT DISTINCT
t1.[SysNr] AS [oldSysNr]
t1.[Year], t1.[WorkTm], t1.[AMSTC],
t1.[AJCC], t1.[ShifNum], t1.[FMN]
FROM
AppData.SCHED AS t1
JOIN
AppData.SCHED AS tS ON t1.ShifNum = tS.ShifNum
AND t1.WorkTM = tS.WorkTM
AND t1.FMN = tS.FMN
WHERE
t1.Year = 2018)
AS t1
JOIN
AppData.SCHED AS tS ON t1.ShifNum = tS.ShifNum
AND t1.WorkTM = tS.WorkTM
AND t1.FMN = tS.FMN
WHERE
t1.Year = 2019;

How do I "dedup" rows based on most recently updated

Lets say I have a table whose content looks like
ID Name Last Update
============================
1 A 1 JAN 2018
1 A 2 JAN 2018
1 A 3 JAN 2018
2 B 3 JAN 2018
2 B 6 JAN 2018
I want to get the result
ID Name Last Update
============================
1 A 3 JAN 2018
2 B 6 JAN 2018
How can I do it?
I tried to group by ID but, how do I get the most recent?
While #Nik's solution can work in situations where there are either no ties for the MAX(date) values (or it doesn't matter which tie value gets selected and whether this produces multiple output rows), an alternative approach is to group all records by ID sort all records belonging to one group by date in descending order and then pick the very first result row per group.
This can be achieved by using the SQL standard window function ROW_NUMBER() like this:
SELECT ID, NAME, DATE
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ID
ORDER BY DATE DESC) RN
, ID
, NAME
, DATE
FROM <TABLE_NAME>
)
WHERE RN = 1;
You could use a query like this to get the results that you need:
SELECT *
FROM table
WHERE (ID, date) IN (SELECT
ID, MAX(Last Update)
FROM table
GROUP BY ID)

Re-Organize Access Table by converting Rows to Columns

I'm pretty new to access and SQL and need some help re-organizing a table. I have the following table (sorry for the table below - having trouble posting):
ID GroupID Distance Code Start_Finish
1 44 7 A S1
2 44 14 A F1
3 45 12 B S1
4 45 16 B F1
5 45 31 C S2
6 45 36 C F2
7 45 81 B S3
8 45 88 B F3
And need for the table to be transformed into:
GroupID Code Start_Distance Finish_Distance
44 A 7 14
45 B 12 16
45 C 31 36
45 B 81 88
try something like this
Select GroupID, Code, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code
If the min and max functions don't give you what you need, try it with First() and Last() instead.
Oops - just noticed you have 2 different entries in the output for GroupID 45 Code B - is that a requirement? With that data structure and requirement, the problem gets much more difficult.
Now I see the final column in the 1st table - I think that can be used to get the output you want:
Select GroupID, Code, mid(start_finish,2) as T, min(distance) as Start_distance, max(distance) as Finish_distance
from Table
group by GroupID, Code, T
You can use conditional aggregation for this.
select GroupID
, CODE
, max(case when Left(Start_Finish, 1) = 'S' then Distance end) as Start_Distance
, max(case when Left(Start_Finish, 1) = 'F' then Distance end) as Finish_Distance
from SomeTable
group by GroupID
, CODE

Getting rowcount for a column based on values from another column

I have two tables, T1 AND T2.
T1 with following columns: Id, TypeofValue, Year, value
Typeofvalue can have 2 values
1 - indicates Actual
2 - indicates Target
T2 With following column: NoOfRecordsToDisplay
I need to fetch the number of records (if existing) for Target corresponding to an Id.
However, the catches are:
Sometimes Target value might not be present for a year
I need to get only last records for targets on the basis of NoOfRecordsToDisplay (The number of records to display comes from T2) for actual
Example1:
NoOfRecordsToDisplay =3, ID =123
The data below should return 3 as we have 3 non null values for target for last 3 years -2015, 2014,2013 in this case
Id TypeofValue Year Value
123 1 2015 55
123 1 2014 56
123 1 2013 57
123 1 2012 58
123 2 2015 50
123 2 2014 50
123 2 2013 50
123 2 2012 50
124 1 2015 55
124 1 2014 56
124 1 2013 57
124 1 2012 58
124 2 2015 50
124 2 2014 50
124 2 2013 50
124 2 2012 50
Another dataset -
NoOfRecordsToDisplay =3, ID =123
The data below should return 0, as we have no values for target for last 3 years -2015, 2014,2013
Id TypeofValue Year Value
123 1 2015 55
123 1 2014 56
123 1 2013 57
123 1 2012 58
123 2 2012 50
124 1 2015 55
124 1 2014 56
124 1 2013 57
124 1 2012 58
124 2 2012 50
OK so if I understand correctly, you want a count of rows where the TypeOfValue = 2, and the Year is in the top n values where TypeOfValue = 1, for a given Id.
This should be:
DECLARE #Id int, #NoOfRecordsToDisplay int
SET #Id = 123
SET #NoOfRecordsToDisplay = 3
SELECT COUNT(*) FROM myTable
WHERE
TypeofValue = 2
AND Id = #Id
AND [Year] IN ( SELECT TOP(#NoOfRecordsToDisplay) [Year] FROM myTable
WHERE TypeofValue = 1 AND Id = #Id
ORDER BY [Year] DESC)
In practice, you would probably want to create this as a stored proc with #Id as an input parameter. #NoOfRecordsToDisplay could either be a parameter too, or selected from some other table - I'm still not 100% clear on this from your question.
Updated SQL Fiddle here: http://sqlfiddle.com/#!3/87b0c/2
Edit: Forgot the ORDER BY on the subquery!
Edit 2: Updated query and SQL fiddle based on updated question.
With SQL and such these queries, understanding the problem and imagination of an algorithm or method for solving the problem is too much important, on base of what you want:
I need to get only last 3 records for targets on the basis of latest 3
values for actual
you need to have tow steps:
1.determine the last 3 years of actual values:
SELECT TOP 3 [Year]
FROM Your_Table
WHERE Typeofvalue = 1
ORDER BY [Year] DESC
2.count the records of target values which their years are in above query:
SELECT COUNT(*) FROM Your_Table
WHERE
Typeofvalue = 2
AND
[Year] IN (
SELECT TOP 3 [Year]
FROM Your_Table
WHERE Typeofvalue = 1
ORDER BY [Year] DESC)
You can do it with join too instead of subquery:
SELECT COUNT(*) FROM Your_Table t
JOIN
(
SELECT TOP 3 [Year]
FROM Your_Table
WHERE Typeofvalue = 1
ORDER BY [Year] DESC
)q
ON T.[Year]=q.[Year]
WHERE t.Typeofvalue=2
You can select the last three actual items and join in the corresponding target items, then count the non-null values:
select
count(Id)
from (
select top 3
t2.Id
from
TheTable t1
left join TheTable t2 on t2.Year = t1.Year and t2.TypeofValue = 2
where
t1.TypeofValue = 1
order by
t1.Year desc
) x