I know we already have a few posts on similar topic. But I think this case a bit different and actually I couldn't get the result I wanted by answers given in other posts.
We have a table as below:
id code amount
------------------
1 A1 80
2 A1 75
3 A1 70
4 A1 70
5 A1 70
1 A2 92
2 A2 85
3 A2 79
4 A2 50
5 A2 50
How can I select the row for "A1" and "A2" based on first lowest value (from top) on "Amount" column? In this case I want the result like below:
id code amount
------------------
3 A1 70
4 A2 50
Thanks!
Use ROW_NUMBER:
SELECT
id, code, amount
FROM (
SELECT *,
Rn = ROW_NUMBER() OVER(PARTITION BY code ORDER BY amount, id)
FROM tbl
) AS t
WHERE Rn = 1
you don't need ordering by id. so correct way would be :
SELECT
id, code, amount
FROM (
SELECT *,
Rn = ROW_NUMBER() OVER(PARTITION BY code ORDER BY amount)
FROM tbl
) AS t
WHERE Rn = 1
Related
I would like to join two tables but couldn't find any existing joins(I tried left, right, full, cross) to do that.
I want to combine table 1 and table 2 into the table 3.
The data order was based on chronological order, and I would like to see the same order in desired table.
TABLE 1:
Student---- Score1
A------------ 90
A------------ 80
B------------ 85
B------------ 60
C------------ 50
C------------ 40
Table2:
Student---- Score2
A------------ 66
A------------ 70
A------------ 85
B------------ 60
C------------ 40
Table 3: Desired Table
Student---- Score1-----Score2
A------------ 90 ----------- 66
A------------ 80 ----------- 70
A------------null -----------85
B------------ 85 ----------- 60
B------------ 60 ----------- null
C------------ 50 ----------- 40
C------------ 40 ----------- null
Thank you!
Ok, we need as first thing try to find a way to add a positional column to your table at runtime. This can be done with ROW_NUMBER() function:
SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Student) Position FROM Table1
SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Student) Position FROM Table2
This creates a nice Position column in our result:
Student Score1 Position
---------- ----------- --------------------
A 90 1
A 80 2
B 85 1
B 60 2
C 50 1
C 40 2
(6 rows affected)
Student Score2 Position
---------- ----------- --------------------
A 66 1
A 70 2
A 85 3
B 60 1
C 40 1
(5 rows affected)
Now we need to join these two temporary results. Since you want to include all the rows from each table, leaving empty (NULL) the spaces left from non-matching rows. FULL OUTER JOIN comes to the rescue, in all its beauty:
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Student) Position FROM Table1) T1
FULL OUTER JOIN
(SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Student) Position FROM Table2) T2
ON T1.Student = T2.Student AND T1.Position = T2.Position
We get this:
Student Score1 Position Student Score2 Position
---------- ----------- -------------------- ---------- ----------- --------------------
A 90 1 A 66 1
A 80 2 A 70 2
NULL NULL NULL A 85 3
B 85 1 B 60 1
B 60 2 NULL NULL NULL
C 50 1 C 40 1
C 40 2 NULL NULL NULL
(7 rows affected)
Now just select what you are interested in:
SELECT COALESCE(T1.student, T2.student) Student,
T1.score1,
T2.score2
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Student) Position FROM Table1) T1
FULL OUTER JOIN
(SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY Student) Position FROM Table2) T2
ON T1.Student = T2.Student AND T1.Position = T2.Position
And voilĂ :
Student score1 score2
---------- ----------- -----------
A 90 66
A 80 70
A NULL 85
B 85 60
B 60 NULL
C 50 40
C 40 NULL
(7 rows affected)
Be aware though: with many records, this could not be the most efficient way of storing and retrieving your data...
Edit: what follows has been added after answer acceptance
Really important: since a small diatribe is born in comments, let's state the obvious.
The database design proposed by OP has many defect, for first it's based on the assumption that the order of the records in the table will always be the one in which the records have been inserted.
This could not be true and my solution can not work as expected until some more robust way of sorting records is implemented.
Would it be better to add a CreatedAt column to both tables, of type datetime, in which to store record insert date:
ALTER TABLE dbo.Table1 ADD
CreatedAt datetime NOT NULL CONSTRAINT DF_Table1_CreatedAt DEFAULT getdate()
ALTER TABLE dbo.Table2 ADD
CreatedAt datetime NOT NULL CONSTRAINT DF_Table2_CreatedAt DEFAULT getdate()
This could allow to more safely order the records.
The solution would change as follows:
SELECT COALESCE(T1.student, T2.student) Student,
T1.score1,
T2.score2
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY CreatedAt) Position FROM Table1) T1
FULL OUTER JOIN
(SELECT *, ROW_NUMBER() OVER (PARTITION BY Student ORDER BY CreatedAt) Position FROM Table2) T2
ON T1.Student = T2.Student AND T1.Position = T2.Position
Well, technically:
with t1 as (
select *, ord = row_number() over(partition by student order by score1 desc)
from table1
),
t2 as (
select *, ord = row_number() over(partition by student order by score2)
from table1
)
select student = isnull(t1.student, t2.student),
t1.score1,
t2.score2
from t1
full join t2 on t1.student = t2.student and t1.ord = t2.ord;
But I doubt your desire is to order scores in table1 in descending order and scores in table2 in ascending order. So you're going to have to pin that down. Is there a column for time the test was administered? Probably best to order by that.
Better still, do this in your front end software, such as with SSRS, crystal reports, or the like. I say this because I assume this is for a reporting need where the rows don't really represent 'records' anymore.
I'm trying to build a query for a matrix table which has a schema like this:
ID 1 2 3
----------- ----------- ----------- -----------
1 13 32 55
2 30 75 129
I want to get the position of a cell according to its coordinate (row number and column number) to create a new table that has the fields row_num, col_num and value
In the example given, this query should return:
row_num col_num value
------- ------- -----------
1 1 13
2 1 30
1 2 32
2 2 75
1 3 55
2 3 129
The query must obtain the value of each cell and return its position X and Y.
I have tried different approach without success. I tried to use UNPIVOT, but it is not showing me the correct information.
Any suggestions are greatly appreciated!
UPDATED:
I added a column whit row number
You need to unpivot the data and generate row number. Here is one way using CROSS APPLY
select Row_number()over(partition by col_num order by ID) as row_num,
col_num,
value
from yourtable
cross apply(values ([1],'1'),([2],'2'),([3],'3')) tc (value,col_num)
To do this using unpivot try this way
select Id,col_num,value
from Yourtable
unpivot
(
value
for col_num in ([1], [2], [3])
) u;
Assuming that you do have a column that specifies the ordering, you can do the calculation as:
select dense_rank() over (order by ??) as row_num,
v.col_num, v.val
from matrix m cross apply
(values (m.col1, 1), (m.col2, 2), (m.col3, 3)
) v(val, col_num);
SQL tables represent unordered sets. The ?? is for whatever column specifies the ordering. If it is already row_num, then you don't need the dense_rank().
For the Updated Question
Declare #YourTable table (ID int,[1] int,[2] int,[3] int)
Insert Into #YourTable values
(1,13,32,55),
(2,30,75,129)
Select A.ID as row_nu,
,B.*
From #YourTable A
Cross Apply (
values (1,A.[1])
,(2,A.[2])
,(3,A.[3])
) B (col_num,value)
Order by B.col_num,A.ID
Returns
row_num col_num value
1 1 13
2 1 30
1 2 32
2 2 75
1 3 55
2 3 129
EDIT - As requested UnPivot
Select ID as row_num ,col_num,value
From #Yourtable
UnPivot (Value for col_num in ([1], [2], [3]) ) B
Order By 2,1
I have this query:
SELECT
Table1.ID, Table1.Code1, Table1.Code2, Table1.Details,
Table1.IDS, Table2.Name
FROM
Table1
INNER JOIN
Table2 ON Table1.Code1 = Table2.Code1
WHERE
Table1.IDS = 1
ORDER BY
Table1.Code1, Table1.Code2
This is my result for query:
ID Code1 Code2 Details IDS Name
1 1001 01 D1 1 N1
2 1001 01 D2 1 N1
3 1001 02 D3 1 N1
4 1001 05 D4 1 N1
5 1002 11 D5 1 N2
6 1002 12 D6 1 N2
7 1005 21 D7 1 N3
8 1005 21 D8 1 N3
But I want this result:
ID Code1 Code2 Details IDS Name
1 1001 01 D1 1 N1
2 01 D2 1
3 02 D3 1
4 05 D4 1
5 1002 11 D5 1 N2
6 12 D6 1
7 1005 21 D7 1 N3
8 21 D8 1
How do I get this result? Please help me. Thanks a lot
Embedding presentation logic in your query isn't ideal. I recommend you process the query results programmatically, either to detect when groups change as you iterate, or to transform the query results into a nested table. The latter can be generalized as a reusable function.
If you can rely on the ID column for ordering the groups (or a combination of other rows, like code1,code2) then you can do this in a few different ways.
If your server is 2012+ then you can use the LAG() window function to access previous rows and if the previous rows Code1 is the same as the current rows Code1 replace it with null (or an empty string if that suits you better). However, if you're using a version < 2012 then you can accomplish it using a self join.
This kind of formatting might be better to handle on the client side (or reporting layer) though if can.
The query below includes both versions, but I commented out the self-join stuff:
SELECT
Table1.ID,
-- CASE WHEN Table1.Code1 = t1.Code1 THEN NULL ELSE Table1.Code1 END AS Code1,
CASE WHEN LAG(Table1.Code1) OVER (ORDER BY Table1.ID) = Table1.Code1 THEN NULL ELSE Table1.Code1 END AS Code1,
Table1.Code2, Table1.Details, Table1.IDS,
-- CASE WHEN Table1.Name = t1.Name THEN NULL ELSE Table1.Name END AS Name,
CASE WHEN LAG(Table2.Name) OVER (ORDER BY Table1.ID) = Table2.Name THEN NULL ELSE Table2.Name END AS Name
FROM
Table1
INNER JOIN
Table2 ON Table1.Code1 = Table2.Code1
-- LEFT JOIN Table1 t1 ON Table1.ID = t1.ID + 1
WHERE
Table1.IDS = 1
ORDER BY
Table1.Code1, Table1.Code2
Sample SQL Fiddle
Morteza,
This is a clear case of a presentation/UI layer requirement. Databases are made for a particular purpose and that is to crunch data and present you with results. I'd highly recommend you to turn to the front end logic for achieving your purpose.
Using ROW_NUMBER() within CTE or a subquery, here is one way to get your expected output:
;WITH q1 as
(
SELECT
t1.ID,
t1.Code1,
t1.Code2,
t1.Details,
t1.IDS,
t2.Name,
ROW_NUMBER() OVER (PARTITION BY t1.Code1 ORDER BY t1.ID) as rn
FROM
table1 t1
INNER JOIN
Table2 t2 ON t1.Code1 = t2.Code1
)
SELECT
q1.ID,
CASE
WHEN rn = 1 THEN q1.Code1
ELSE ''
END as Code1, --only populate first row for each code1
q1.Code2,
q1.Details,
q1.IDS,
CASE
WHEN rn = 1 THEN q1.Name
ELSE ''
END as Name --only populate first row for each name
FROM
q1
WHERE
q1.IDS = 1
ORDER BY
q1.Code1, q1.Code2
SQL Fiddle Demo
I have a select * query which gives lots of row and lots of columns of results. I have an issue with duplicates of one column A when given the same value of another column B that I would like to only include one of.
Basically I have a column that tells me the "name" of object and another that tells me the "number". Sometimes I have an object "name" with more than one entry for a given object "number". I only want distinct "numbers" within a "name" but I want the query to give the entire table when this is true and not just these two columns.
Name Number ColumnC ColumnD
Bob 1 93 12
Bob 2 432 546
Bob 3 443 76
This example above is fine
Name Number ColumnC ColumnD
Bob 1 93 12
Bob 2 432 546
Bill 1 443 76
Bill 2 54 1856
This example above is fine
Name Number ColumnC ColumnD
Bob 1 93 12
Bob 2 432 546
Bob 2 209 17
This example above is not fine, I only want one of the Bob 2's.
Try it if you are using SQL 2005 or above:
With ranked_records AS
(
select *,
ROW_NUMBER() OVER(Partition By name, number Order By name) [ranked]
from MyTable
)
select * from ranked_records
where ranked = 1
If you just want the Name and number, then
SELECT DISTINCT Name, Number FROM Table1
If you want to know how many of each there are, then
SELECT Name, Number, COUNT(*) FROM Table1 GROUP BY Name, Number
By using a Common Table Expression (CTE) and the ROW_NUMBER OVER PARTION syntax as follows:
WITH
CTE AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Name, Number ORDER BY Name, Number) AS R
FROM
dbo.ATable
)
SELECT
*
FROM
CTE
WHERE
R = 1
WITH
CTE AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Plant, BatchNumber ORDER BY Plant, BatchNumber) AS R
FROM dbo.StatisticalReports WHERE dbo.StatisticalReports. \!"FermBatchStartTime\!" >= DATEADD(d,-90, getdate())
)
SELECT
*
FROM
CTE
WHERE
R = 1
ORDER BY dbo.StatisticalReports.Plant, dbo.StatisticalReports.FermBatchStartTime
I have a Data like this
Id TagNo CoreNo FromLocation Device FromTerminal
1 1000 1 AA A1 11
2 1000 2 AA A1 12
3 1000 3 AA A2 13
4 1000 4 AA A2 14
5 1001 1 BB T1 10
I want to have this
TagNo CoreNo FromLocation Device FromTerminal
1000 1 AA A1 11
2 12
3 A2 13
4 14
1001 1 BB T1 10
how can i have it in TSQL / linq?
Formatting the data for display is a job for the application using the data, not the database. There are no specific commands in T-SQL to do what you want.
It can be done, though this is usually handled by a reporting engine (or such).
The following query creates a row number (ROW_NUMBER) per group of duplicate values. Then it only selects the value if it's corresponding row number is 1 by using a CASE.
with mt as (
select
ROW_NUMBER() over (partition by tagNo order by Id) as TagRowNr,
ROW_NUMBER() over (partition by tagNo,FromLoc order by Id) as FromLocRowNr,
ROW_NUMBER() over (partition by tagNo,Device order by Id) as DeviceRowNr,
Id, TagNo, Core, FromLoc, Device, FromTerm
from MyData
)
select
Case when TagRowNr=1 then TagNo else '' end as TagNo,
CoreNo,
Case when FromLocRowNr=1 then FromLoc else '' end as FromLoc,
Case when DeviceRowNr=1 then Device else '' end as Device,
FromTerm
from mt
Order by Id