How to group a couple of rows in SQL Server? - sql-server

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

Related

Joining two SQL tables based on the equality of few columns

I am trying to Create a SQL View by joining two SQL tables and return only the lowest value from second table and all the rows from first table similar to left join.
My problem can be clearly explained with the below example.
Table1
Id Product Grade Term Bid Offer
100 ABC A Q1 10 20
101 ABC A Q1 5 25
102 XYZ A Q2 25 30
103 XYZ B Q2 20 30
Table2
Id Product Grade Term TradeValue
1 ABC A Q1 100
2 ABC A Q1 95
3 XYZ B Q2 100
In the above data I want to join Table1 and Table2 when ever the columns Product,Grade and Term from both the tables are equal and return all the rows from Table1 while joining the lowest Value of the column TradeValue from Table2 to the first record of the match and making TradeValue as NULL for other rows of the resultant View and the resultant View should have the Id of Table2 as LTID
So the resultant SQL View should be
RESULT
Id Product Grade Term Bid Offer TradeValue LTID
100 ABC A Q1 10 20 95 2
101 ABC A Q1 5 25 NULL 2
102 XYZ A Q2 25 30 NULL NULL
103 XYZ B Q2 20 30 100 3
I tried using the following query
CREATE VIEW [dbo].[ViewCC]
AS
SELECT
a.Id,a.Product,a.Grade,a.Term,a.Bid,a.Offer,
b.TradeValue
FROM Table1 AS a
left JOIN (SELECT Product,Grade,Term,MIN(TradeValue) TradeValue from Table2 Group by Product,Grade,Term,) AS b
ON b.Product=a.Product
and b.Grade=a.Grade
and b.Term=a.Term
GO
The above Query returned the following data which is apt to the query I wrote but that is not what I was trying to get
Id Product Grade Term Bid Offer TradeValue
100 ABC A Q1 10 20 95
101 ABC A Q1 5 25 95 --This should be null
102 XYZ A Q2 25 30 NULL
103 XYZ B Q2 20 30 100
As we can see minimum value of TradeValue being assigned to all matching rows in Table1 and also I was not able to return Id As LTID from Table2 as I have issues with group by clause as I cannot group it by b.Id as it returns too many rows.
May I know a better way to deal with this?
You need a row number attached to each record from Table1, so that the requirement of only joining the first record from each group of Table1 can be fulfilled:
CREATE VIEW [dbo].[ViewCC]
AS
SELECT a.Id, a.Product, a.Grade, a.Term, a.Bid, a.Offer,
b.TradeValue, b.Id AS LTID
FROM (
SELECT *, ROW_NUMBER() OVER(PARTITION BY Product, Grade, Term ORDER BY Id) AS rn
FROM Table1
) a
OUTER APPLY (
SELECT TOP 1 CASE WHEN rn = 1 THEN TradeValue
ELSE NULL
END AS TradeValue, Id
FROM Table2
WHERE Product=a.Product AND Grade=a.Grade AND Term=a.Term
ORDER BY TradeValue) b
GO
OUTER APPLY returns a table expression containing either the matching record from Table2 with the lowest TradeValue, or NULL if no matching record exists.

To count rows that is NOT NULL in SSRS

I want to number some base rows in table without mixing the ordering. I have table like this:
Status ProductId
A 12
NULL 25
B 35
C 56
NULL 89
NULL 99
D 120
E 140
I want to add No column, to count Statuses which is not null with same ProductId ordering, but, don't want to count NULL rows. I want the result like this:
No Status ProductId
1 A 12
NULL 25
2 B 35
3 C 56
NULL 89
NULL 99
4 D 120
5 E 140
I work on SQL Sever 2008, SSRS. Someone can give solution in SQL side or in RDL file.
You can do this:
WITH CTE
AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY Status) AS No,
Status, ProductId
FROM table1
WHERE Status IS NOT NULL
)
SELECT
c.No,
t.Status,
t.ProductId
FROM table1 AS t
LEFT JOIN CTE AS c ON c.ProductId = t.ProductId
ORDER BY ProductId;
SQL Fiddle Demo
You can use this query directly in your report.

Processing on TSQL Command

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

Select a distinct record, filtering is not working

Hello EVery I am new to SQl. query to result in the following records.
I have a table with records as
c1 c2 c3 c4 c5 c6
1 John 2.3.2010 12:09:54 4 7 99
2 mike 2.3.2010 13:09:59 8 6 88
3 ahmad 2.3.2010 13:09:59 1 9 19
4 Jim 23.3.2010 16:35:14 4 5 99
5 run 23.3.2010 12:09:54 3 8 12
I want to fecth only records.
i.e only 1 latest record per day. If both of them happen at the same time, sort by C1.so in 1&3 it should fetch 3.
3 ahmad 2.3.2010 14:09:59 1 9 19
4 Jim 23.3.2010 16:35:14 4 5 99
I have got a new problem in this.
If i filter the records based on conditions the last record is missing. I tried many ways but still it is failing. Here update_log is my table.
SELECT * FROM update_log t1
WHERE (t1.c3) =
(
SELECT MAX(t2.c3)
FROM update_log t2
WHERE DATEDIFF(dd,t2.c3, t1.c3) = 0
)
and t1.c3 > '02.03.2010' and t1.modified_at <= '22.03.2010'
ORDER BY t1.c3 ASC. But i am not able to retrieve the record
4 Jim 23.3.2010 16:35:14 4 5 99
I dont know this query results in only
3 ahmad 2.3.2010 14:09:59 1 9 19
The format of the column c3 is datetime. I am pumping the data into the column as
using $date = date("d.m.Y H:i",time()); -- simple date fetch of today.
Another query that i tried for the same purpose.
select * from (select convert(varchar(10), c3,104) as date, max(c3) as max_date, max(c1) as Nr from update_log group by convert(varchar(10), c3,104)) as t2 inner join update_log as t1 on (t2.max_date = t1.c3 and convert(varchar(10), c3,104) = date and t1.[c1]= Nr) WHERE t1.c3 >= '02.03.2010' and t1.c3 <= '16.04.2010' . I even tried this way..the same error last record is not coming..
Following steps should produce the results you are after
Find max c3 for every day.
Join the results with your original table, witholding only the max c1 values.
SQL Statement (Edited)
DECLARE #update_log TABLE (c1 INTEGER, c3 DATETIME)
INSERT INTO #update_log
SELECT 1, '3.2.2010 12:09:54'
UNION ALL SELECT 2, '3.2.2010 13:09:59'
UNION ALL SELECT 3, '3.2.2010 13:09:59'
UNION ALL SELECT 4, '3.23.2010 16:35:14'
UNION ALL SELECT 5, '3.23.2010 12:09:54'
SELECT c1 = MAX(l.c1), l.c3
FROM #update_log l
INNER JOIN (
SELECT c3_max = MAX(c3)
FROM #update_log
WHERE c3 > '3.2.2010 00:00:00'
AND c3 < '3.24.2010 00:00:00'
GROUP BY
CONVERT(VARCHAR(10), c3, 101)
) l_maxdate ON l_maxdate.c3_max = l.c3
GROUP BY
l.c3
Notes
You should read the FAQ regarding as to how this site operates. As has been mentioned, Stack Overflow is not intented to be used like one would use a newsreader where you ask follow-up questions after an answer has been given by creating a new answer.
You should edit your question for any additional information or use the comments. If the additional information is that much that it in effect changes the entire question, you should consider making a new question out of it.
That being said, enjoy SO.
Assuming c1 is unique, hopefully even the primary key. This also assumes the use of SQL Server 2008 for the DATE data type.
SELECT t1.*
FROM update_log t1
WHERE t1.c3 > '02.03.2010'
AND t1.modified_at <= '22.03.2010'
AND t1.c1 IN
( SELECT TOP 1 c1
FROM update_log t2
WHERE CAST(t1.c3 As DATE) = CAST(t2.c3 As DATE)
ORDER BY c3 DESC, c1 DESC
)
OK, Now i understood. actually i wrote the query for the same purpose this way.
select * from #temp
select * from
(select max(c1) as nr from
(select convert(varchar(10), c3,104) as date, max(c3) as max_date
from #temp where
convert(varchar(10),c3,104) >= '02.02.2010' and
convert(varchar(10),c3,104) <= '23.02.2010'
group by convert(varchar(10), c3,104))
as t2 inner join #temp as t1 on (t2.max_date = t1.c3 and
convert(varchar(10), c3,104) = date)
group by convert(varchar(10),max_date,104))
as t3 inner join #temp as t4 on (t3.nr = t4.c1 )
If i change these 2 lines to c3 >= '02.02.2010' and
c3 <= '24.02.2010'. It is working fine. but the query that i have posted is not able to filter the records properly based on dates.
I want to know where i went to wrong to enhance my knoweldge rather than just copying ur query:-)

SQL Server + Select top 1 record of all the distinct records

I am struggling to write a query to result in the following records.
I have a table with records as
c1 c2 c3 c4 c5 c6
1 John 2.3.2010 12:09:54 4 7 99
2 mike 2.3.2010 13:09:59 8 6 88
3 ahmad 2.3.2010 14:09:59 1 9 19
4 Jim 23.3.2010 16:35:14 4 5 99
5 run 23.3.2010 12:09:54 3 8 12
I want to fetch only the records :-
3 ahmad 2.3.2010 14:09:59 1 9 19
4 Jim 23.3.2010 16:35:14 4 5 99
I mean the records that are sort by column c3 and the one which is latest for that day. here i have 1, 2, 3 records that are at different times of the day. there i need the records that are sort by date desc and then only top 1 record. similarly for 4 and 5. can you please help me in writing a query.
If you're on SQL Server 2008 or 2008 R2, you can try this:
WITH TopPerDay AS
(
SELECT
c1, c2, c3, c4, c5, C6,
ROW_NUMBER() OVER
(PARTITION BY CAST(c3 AS DATE) ORDER BY c3 DESC) 'RowNum'
FROM dbo.YourTable
)
SELECT *
FROM TopPerday
WHERE RowNum = 1
I basically partition the data by day (using the DATE type in SQL Server 2008 and up), and order by the c3 column in a descending order. This means, for every day, the oldest row will have RowNum = 1 - so I just select those rows from the Common Table Expression and I'm done.
Tried this on a SQL Server 2005 database.
SELECT *
FROM dbo.YourTable t1
WHERE (t1.c3) =
(
SELECT MAX(t2.c3)
FROM dbo.YourTable t2
WHERE DATEDIFF(dd,t2.c3, t1.c3) = 0
)
ORDER BY t1.c3 ASC
Thanks for the responses!
I have found the solution too.
select * from
(select convert(varchar(10),c3,104) as date, max(c3) as date1 from MYTABLE
group by convert(varchar(10),c3,104)) as T1 innerjoin MYTABLE as T2 on
convert(varchar(10),T2.c3,104) = T1.date and t2.c3 = T2.date1

Resources