Concatenate from rows in SQL server - sql

I want to concatenate from multiple rows
Table:
|id |Attribute |Value |
|--------|------------|---------|
|101 |Manager |Rudolf |
|101 |Account |456 |
|101 |Code |B |
|102 |Manager |Anna |
|102 |Cardno |123 |
|102 |Code |B |
|102 |Code |C |
The result I’m looking for is:
|id |Manager|Account|Cardno|Code |
|--------|-------|-------|------|----------|
|101 |Rudolf |456 | |B |
|102 |Anna | |123 |B,C |
I have the following code from a related question:
select
p.*,
a.value as Manager,
b.value as Account,
c.value as Cardno
from table1 p
left join table2 a on a.id = p.id and a.attribute = 'Manager'
left join table2 b on b.id = p.id and b.attribute = 'Account'
left join table2 c on c.id = p.id and b.attribute = 'Cardno'
However, it fails for the Code attribute with ID# 102, where both B and C values are present.
How can I update this to include both of those values in the same result?

If you are using SQL SERVER 2017 or above then string_agg() with PIVOT() will be easy to use but much faster in performance solution (Query#1).
If you are using older version of SQL Server then go for Query#2 with STUFF() and XML PATH FOR() for concatenating value along with PIVOT()
Schema:
create table table1 (id int, Attribute varchar(50) , Value varchar(50));
insert into table1 values(101 ,'Manager' ,'Rudolf');
insert into table1 values(101 ,'Account' ,'456');
insert into table1 values(101 ,'Code' ,'B');
insert into table1 values(102 ,'Manager' ,'Anna');
insert into table1 values(102 ,'Cardno' ,'123');
insert into table1 values(102 ,'Code' ,'B');
insert into table1 values(102 ,'Code' ,'C');
GO
Query#1 PIVOT() with STRING_AGG():
select *
from
(
select t1.id,t1.attribute,
string_agg(value,',') AS value
from table1 t1
group by t1.id,t1.attribute
) d
pivot
(
max(value)
for attribute in (manager,account,cardno,code)
) piv
Output:
id
manager
account
cardno
code
101
Rudolf
456
<emnull</em
B
102
Anna
<emnull</em
123
B,C
Query#2 PIVOT() WITH STUFF() AND XML PATH FOR():
select *
from
(
select distinct t1.id,t1.attribute,
STUFF(
(SELECT ', ' + convert(varchar(10), t2.value, 120)
FROM table1 t2
where t1.id = t2.id and t1.attribute=t2.attribute
FOR XML PATH (''))
, 1, 1, '') AS value
from table1 t1
) d
pivot
(
max(value)
for attribute in (manager,account,cardno,code)
) piv
Output:
id
manager
account
cardno
code
101
Rudolf
456
<emnull</em
B
102
Anna
<emnull</em
123
B, C
db<fiddle here

Another method via XML and XQuery.
It is for SQL Server 2008 onwards.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (ID INT, attribute VARCHAR(20), [Value] VARCHAR(30));
INSERT INTO #tbl (ID, attribute, Value) VALUES
(101,'Manager','Rudolf'),
(101,'Account','456'),
(101,'Code','B'),
(102,'Manager','Anna'),
(102,'Cardno','123'),
(102,'Code','B'),
(102,'Code','C');
-- DDL and sample data population, end
;WITH rs AS
(
SELECT ID, (
SELECT *
FROM #tbl AS c
WHERE c.id = p.id
FOR XML PATH('r'), TYPE, ROOT('root')
) AS xmldata
FROM #tbl AS p
GROUP BY id
)
SELECT ID
, COALESCE(xmldata.value('(/root/r[attribute="Manager"]/Value/text())[1]','VARCHAR(30)'),'') AS Manager
, COALESCE(xmldata.value('(/root/r[attribute="Account"]/Value/text())[1]','VARCHAR(30)'),'') AS Account
, COALESCE(xmldata.value('(/root/r[attribute="Cardno"]/Value/text())[1]','VARCHAR(30)'),'') AS Cardno
, COALESCE(REPLACE(xmldata.query('data(/root/r[attribute="Code"]/Value)').value('.', 'VARCHAR(MAX)'), SPACE(1), ','),'') AS Code
FROM rs
ORDER BY ID;
Output
+-----+---------+---------+--------+------+
| ID | Manager | Account | Cardno | Code |
+-----+---------+---------+--------+------+
| 101 | Rudolf | 456 | | B |
| 102 | Anna | | 123 | B,C |
+-----+---------+---------+--------+------+

UPD: "STRING_AGG only Server 2017+"
You can solve this task using CTE and STRING_AGG function, for example:
declare
#t table (id int, Attribute varchar (100), [Value] varchar (100) )
insert into #t
values
(101, 'Manager', 'Rudolf'),
(101, 'Account', '456'),
(101, 'Code', 'B'),
(102, 'Manager', 'Anna'),
(102, 'Cardno', '123'),
(102, 'Code', 'B'),
(102, 'Code', 'C')
;with cte as
(
select id, Attribute
,STRING_AGG([Value], ', ') WITHIN GROUP (ORDER BY ID ASC) AS [Value]
from #t
group by ID, Attribute
)
select
max(p.ID) ID
,a.Value Manager
,isnull(b.Value, '') Account
,isnull(c.Value, '') Cardno
,isnull(e.Value, '') Code
from cte p
left join cte a on a.id =p.ID and a.attribute = 'Manager'
left join cte b on b.id = p.id and b.attribute = 'Account'
left join cte c on c.id = p.id and c.attribute = 'Cardno'
left join cte e on e.id = p.id and e.attribute = 'Code'
group by p.ID, a.Value,b.Value,c.Value,e.Value

Related

How to use Group by format for XML format data in SQL server

I have this data with me.
declare #T table
(
ID int,
[subject] varchar(30),
Marks int
)
insert into #T values
(1, 'Maths',78),
(1, 'Science',89),
(2, 'Maths',90),
(3, 'Maths',91),
(4, 'Maths',92)
I tried this query:
SELECT ID
,(SELECT t1.* FOR XML PATH('body'),TYPE) AS TheRowAsXml
FROM #T as t1
But it is giving 1 Row for every Entry.
I want to group by ID
My expected output is like:
<body>
<ID>1</ID>
<subject>Maths</subject>
<Marks>78</Marks>
<subject>Science</subject>
<Marks>89</Marks>
</body>
For ID 1 and so on
Please any suggestion would be appreciated
You can generate using subqueries, as given below:
declare #T table
(
ID int,
[subject] varchar(30),
Marks int
)
insert into #T values
(1, 'Maths',78),
(1, 'Science',89),
(2, 'Maths',90),
(3, 'Maths',91),
(4, 'Maths',92)
--SELECT id, subject, marks from #T WHERE id = 1
--for xml path('')
SELECT distinct id,CONCAT('<body><ID>',cast(id as varchar(10)),'</ID>',
(SELECT subject, marks from #T WHERE id = t.id
for xml path('')
),'</body>') as Rowxml
from #t as t
+----+-------------------------------------------------------------------------------------------------------------+
| id | Rowxml |
+----+-------------------------------------------------------------------------------------------------------------+
| 1 | <body><ID>1</ID><subject>Maths</subject><marks>78</marks><subject>Science</subject><marks>89</marks></body> |
| 2 | <body><ID>2</ID><subject>Maths</subject><marks>90</marks></body> |
| 3 | <body><ID>3</ID><subject>Maths</subject><marks>91</marks></body> |
| 4 | <body><ID>4</ID><subject>Maths</subject><marks>92</marks></body> |
+----+-------------------------------------------------------------------------------------------------------------+
UPDATE
one more cleaner approach
SELECT id "ID",max(t2.rowxml)
from #t as t
CROSS APPLY
(SELECT subject, marks
from #T WHERE id = t.id
for xml path('')
) as t2(rowxml)
group by t.id
for xml path('body')

Joining tables containing comma delimited values

I have three excel sheet I push them into tables in SQL server and I need to join these table. However, I believe - as I have tried already - normal join wouldn't work. I have programming background but not that much with SQL.
Table1
ID Data_column reference_number
1 some data 1528,ss-456
2 some data 9523
3 some data ss-952
4 some data null
Table2
ID Data_column
ss-456 some data
ss-952 some data
Table3
ID Data_column
1528 some data
9523 some data
In the case below How I will be able to join this raw on both table.
Table1
ID Data_column reference_number
1 some data 1528,ss-456
declare #t1 as table(
id int
,data_column varchar(20)
,reference_number varchar(20)
)
declare #t2 as table(
id varchar(20)
,data_column varchar(20)
)
declare #t3 as table(
id varchar(20)
,data_column varchar(20)
)
insert into #t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);
insert into #t2 values('ss-456','some data'),('ss-952','some data');
insert into #t3 values(1528,'some data'),(9523,'some data');
Quick solution
select * from #t1 t1
left outer join #t2 t2 on t1.reference_number like '%'+t2.id or t1.reference_number like t2.id+'%'
left outer join #t3 t3 on t1.reference_number like '%'+t3.id or t1.reference_number like t3.id+'%'
Result (left join):
id data_column reference_number id data_column id data_column
1 some data 1528,ss-456 ss-456 some data 1528 some data
2 some data 9523 NULL NULL 9523 some data
3 some data ss-952 ss-952 some data NULL NULL
4 some data NULL NULL NULL NULL NULL
You can change 'left outer join' to 'inner join' for exact match.
Clumsy design, clumsy solution:
SELECT *
FROM Table1
INNER JOIN Table2 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table2.ID + ',%'
INNER JOIN Table3 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table3.ID + ',%'
You must append leading and trailing commas to make sure that, for example, 1528,ss-456asdf does not match %ss-456%.
I see two problems here. First is the inconsistent type of ID in table 2 and 3 and aggregation of referenced keys in table 1. Here is an example how to solve both problems. To split REFERENCE_NUMBER column I used STRING_SPLIT function.
Update:
I added the solution which should work with SQL Server 2012.
I assumed that you wish to join data from table 1 with 2 or 3 depending in existence of this data. This is just my idea what you wanted to achive.
-- data preparing
declare #t1 as table(
id int
,data_column varchar(20)
,reference_number varchar(20)
)
declare #t2 as table(
id varchar(20)
,data_column varchar(20)
)
declare #t3 as table(
id int
,data_column varchar(20)
)
insert into #t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);
insert into #t2 values('ss-456','some data'),('ss-952','some data');
insert into #t3 values(1528,'some data'),(9523,'some data');
-- Solution example version >= 2016
with base as (
select t1.id,t1.data_column,f1.value from #t1 t1 outer apply string_split(t1.reference_number,',') f1)
select b.id,b.data_column,b.value,t2.data_column from base b join #t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join #t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;
-- Solution for SQL Version < 2016
with base as (
select t1.id,t1.data_column,f1.value from #t1 t1 outer apply(
SELECT Split.a.value('.', 'NVARCHAR(MAX)') value
FROM
(
SELECT CAST('<X>'+REPLACE(t1.reference_number, ',', '</X><X>')+'</X>' AS XML) AS String
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)
) f1)
select b.id,b.data_column,b.value,t2.data_column from base b join #t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join #t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;
You will require a function to divide the comma separated sting into rows. If you don't have access to thr inbuilt string_split() function (as of mssql 2017 with compatibility of 130) there are several to choose from here
CREATE TABLE table1(
ID INTEGER NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
,reference_number VARCHAR(11)
);
INSERT INTO table1(ID,Data_column,reference_number) VALUES
(1,'t1somedata','1528,ss-456')
, (2,'t1somedata','9523')
, (3,'t1somedata','ss-952')
, (4,'t1somedata',NULL);
CREATE TABLE table2(
ID VARCHAR(6) NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table2(ID,Data_column) VALUES
('ss-456','t2somedata'),
('ss-952','t2somedata');
CREATE TABLE table3(
ID VARCHAR(6) NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table3(ID,Data_column) VALUES
('1528','t3somedata'),
('9523','t3somedata');
I have used this splistring function, but you can use almost any of the many freely available.
CREATE FUNCTION dbo.SplitStrings_Moden
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b),
cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(#List,1)))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
WHERE (SUBSTRING(#List,t.N,1) = #Delimiter OR t.N = 0))
SELECT Item = SUBSTRING(#List, s.N1, ISNULL(NULLIF(CHARINDEX(#Delimiter,#List,s.N1),0)-s.N1,8000))
FROM cteStart s;
This is what the data looks like using the splitstring function:
select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
ID | Data_column | reference_number | Item
-: | :---------- | :--------------- | :-----
1 | t1somedata | 1528,ss-456 | 1528
1 | t1somedata | 1528,ss-456 | ss-456
2 | t1somedata | 9523 | 9523
3 | t1somedata | ss-952 | ss-952
4 | t1somedata | null | null
and now joining to the other tables:
select
*
from (
select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
) t1
left join table2 on t1.item = table2.id
left join table3 on t1.item = table3.id
where t1.item is not null
GO
ID | Data_column | reference_number | Item | ID | Data_column | ID | Data_column
-: | :---------- | :--------------- | :----- | :----- | :---------- | :--- | :----------
1 | t1somedata | 1528,ss-456 | 1528 | null | null | 1528 | t3somedata
1 | t1somedata | 1528,ss-456 | ss-456 | ss-456 | t2somedata | null | null
2 | t1somedata | 9523 | 9523 | null | null | 9523 | t3somedata
3 | t1somedata | ss-952 | ss-952 | ss-952 | t2somedata | null | null
db<>fiddle here
TRY THIS: If your reference_number is fixed and always stored IDs upto 2 only then you can go with the below approach
SELECT *
FROM(
SELECT ID,
data_column,
CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN
SUBSTRING(reference_number, PATINDEX ( '%,%', reference_number)+1, LEN(reference_number))
ELSE reference_number END AS ref_col
FROM #table1
UNION
SELECT ID,
data_column,
CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN
SUBSTRING(reference_number, 0, PATINDEX ( '%,%', reference_number))
END
FROM #table1) t1
LEFT JOIN #table2 t2 ON t2.id = t1.ref_col
LEFT JOIN #table3 t3 ON t3.id = t1.ref_col
WHERE t1.ref_col IS NOT NULL
OUTPUT:
ID data_column ref_col ID Data_column ID Data_column
1 some data 1528 NULL NULL 1528 some data
1 some data ss-456 ss-456 some data NULL NULL
2 some data 9523 NULL NULL 9523 some data
3 some data ss-952 ss-952 some data NULL NULL
4 some data null NULL NULL NULL NULL
You can Implement and get desired result using Substring and charIndex functions on the reference_number.
I upvoted an answer of 'is_oz' since i used his ready made schema to test and build a query for you.
below is the final query i build after several tries i made here:
select * from abc
left join abc2 on abc2.id = case when charindex(',',abc.reference_number) > 0
then substring(abc.reference_number
,charindex(',',abc.reference_number)+1
,len(abc.reference_number)-(charindex(',',abc.reference_number)-1)
)
else abc.reference_number
end
left join abc3 on abc3.id = case when charindex(',',abc.reference_number) > 0
then substring(abc.reference_number
,0
,(charindex(',',abc.reference_number))
)
else abc.reference_number
end
As per your requirement as much as i understand, it is returning all the matched rows from 2 other tables but still i hope this fulfills all the requirements you seek in your question. :)

Multiple counts on a single column SQL

I am currently running a query like the following:
SELECT a.ID, a.ContactID, a.Code,
FROM tableA a
JOIN (SELECT ContactID, Code
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1) b
ON (a.Code = b.Code AND a.ContactID = b.ContactID)
WHERE a.ContactID IS NOT NULL
ORDER BY a.Code
This returns data that looks like the folloing:
table : a
+-------+-----------+-----------+
| ID | ContactID | Code |
+-------+-----------+-----------+
| 1 | 111 | abcd2 |
| 2 | 111 | abcd2 |
| 3 | 222 | abcd1 |
| 4 | 222 | abcd1 |
| 5 | 222 | abcd1 |
| 6 | 222 | abcd1 |
+-------+-----------+-----------+
So as you can see I get ContactID's that have more then one of the same Code.
The problem with this is, is that I don't want all this output (real table is much larger). I want a COUNT to go along side the Code column and just show one row for each iteration of Code. Like the following:
+-------+-----------+-----------+------+
| ID | ContactID | Code |COUNT |
+-------+-----------+-----------+------+
| 1 | 111 | abcd2 | 2 |
| 3 | 222 | abcd1 | 4 |
+-------+-----------+-----------+------+
Any help on this would be great and I hope I have explained my problem well enough. If not please ask for more information and if this has been answered before please point in that direction.
Thanks.
Your solution and other answers are way to complicated, you don't need the self join when you're simply aggregating with HAVING Count(x) > 1:
SELECT MIN(ID), ContactID, Code, COUNT(Code) AS [COUNT]
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1
Full solution:
SQL Fiddle
CREATE TABLE TableA
([ID] int, [ContactID] int, [Code] varchar(5))
;
INSERT INTO TableA
([ID], [ContactID], [Code])
VALUES
(1, 111, 'abcd2'),
(2, 111, 'abcd2'),
(3, 222, 'abcd1'),
(4, 222, 'abcd1'),
(5, 222, 'abcd1'),
(6, 222, 'abcd1')
;
Query 1:
SELECT min(id), ContactID, Code, count(Code) as [COUNT]
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1
Results:
| | ContactID | Code | |
|---|-----------|-------|---|
| 1 | 111 | abcd2 | 2 |
| 3 | 222 | abcd1 | 4 |
I would use exists instead of subquery :
select min(a.id) as id, a.ContactID, a.Code, count(*) as Cnt
from tableA a
where exists (select 1
from tableA a1
where a1.ContactID = a.ContactID and
a1.Code = a.Code and
a1.id <> a.id
)
group by a.ContactID, a.Code;
sub-query
select min(ID) as id, ContactID,Code,count(*) as cnt from
(SELECT a.ID, a.ContactID, a.Code
FROM tableA a
JOIN (SELECT ContactID, Code
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1) b
ON (a.Code = b.Code AND a.ContactID = b.ContactID)
WHERE a.RetailContactID IS NOT NULL
ORDER BY a.Code
) t group ContactID,Code
;WITH CTE AS
(
SELECT a.ID, a.ContactID, a.Code,
FROM tableA a
JOIN (SELECT ContactID, Code
FROM tableA
WHERE ContactID IS NOT NULL
GROUP BY Code, ContactID
HAVING COUNT(Code) > 1) b
ON (a.Code = b.Code AND a.ContactID = b.ContactID)
WHERE a.RetailContactID IS NOT NULL
)
SELECT ID, ContactID, Code, COUNT(*) AS Cnt
FROM CTE
GROUP BY ID, ContactID, Code
ORDER BY 1, 2, 3
Extend your SQL query with one more grouping:
SELECT min(a.ID), a.ContactID, a.Code, count(*)
...
GROUP BY a.ContactID, a.Code
ORDER BY a.Code
Use group by in your select query
select x.ContactID, x.Code, [count] = count(x.id) from (
select id = 1 , ContactID = 111 , Code = 'abcd2' union all
select 2 , 111 , 'abcd2' union all
select 3 , 222 , 'abcd1' union all
select 4 , 222 , 'abcd1' union all
select 5 , 222 , 'abcd1' union all
select 6 , 222 , 'abcd1') x group by x.Code, x.ContactID

Remove duplicate rows from joined table

I have following sql query
SELECT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
Which provide following result
School| avgscore
xyz | 5
xyz | 5
xyz | 5
abc | 3
abc | 3
kkk | 1
My question is how to remove those duplicates and get only following.
School| avgscore
xyz | 5
abc | 3
kkk | 1
I tried with
SELECT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
group by m.School
But it gives me following error
"Column 'postswithratings.avgscore' is invalid in the select list
because it is not contained in either an aggregate function or the
GROUP BY clause."
No need to make things complicated. Just go with:
SELECT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
group by m.School, c.avgscore
or
SELECT DISTINCT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
You have to only add distinct keyword like this :-
SELECT DISTINCT m.School, c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode
CREATE TABLE #Table2
([School] varchar(3), [avgscore] int)
INSERT INTO #Table2
([School], [avgscore])
VALUES
('xyz', 5),
('xyz', 5),
('xyz', 5),
('abc', 3),
('abc', 3),
('kkk', 1)
;
SELECT SCHOOL,AVGSCORE FROM (SELECT *,ROW_NUMBER() OVER( PARTITION BY [AVGSCORE] ORDER BY (SELECT NULL)) AS RN FROM #TABLE2)A
WHERE RN=1
ORDER BY AVGSCORE
-------
;WITH CTE AS
(SELECT *,ROW_NUMBER() OVER( PARTITION BY [AVGSCORE] ORDER BY (SELECT NULL)) AS RN FROM #TABLE2)
SELECT SCHOOL,AVGSCORE FROM CTE WHERE RN=1
output
SCHOOL AVGSCORE
kkk 1
abc 3
xyz 5
Using the DISTINCT keyword will make sql use sets instead of multisets. So values only appear once
This will delete the Duplicate rows (Only Duplicate)
Schema:
CREATE TABLE #TAB (School varchar(5) , avgscore int)
INSERT INTO #TAB
SELECT 'xyz', 5
UNION ALL
SELECT 'xyz', 5
UNION ALL
SELECT 'xyz', 5
UNION ALL
SELECT 'abc', 3
UNION ALL
SELECT 'abc', 3
UNION ALL
SELECT 'kkk', 1
Now use CTE as your Tempprary View and delete the data.
;WITH CTE AS(
SELECT ROW_NUMBER() OVER (PARTITION BY School,avgscore ORDER BY (SELECT 1)) DUP_C,
School, avgscore FROM #TAB
)
DELETE FROM CTE WHERE DUP_C>1
Now do check #TAB, the data will be
+--------+----------+
| School | avgscore |
+--------+----------+
| xyz | 5 |
| abc | 3 |
| kkk | 1 |
+--------+----------+
you only use group by if you're using aggregated function, eg. max. sum, avg
in that case,
SELECT Distinct(m.School), c.avgscore
FROM postswithratings c
join ZEntrycriteria on c.fk_postID= m.schoolcode

Inner Join + select the most recent

I have been trying to do the bellow query but honestly it's driving me crazy.
I have 2 Tables on MS SQL CE 4.0
Table 1 Name: Items
ID
Item_Code
Logged_by
Description
ID | Item_Code | Logged_by | Description
1 | A | Pete | just an A
2 | B | Mary | Seams like a B
3 | C | Joe | Obviously this is a C
4 | D | Pete | This is another A
Table 2 Name: Item_Comments
ID
Item_Code
Comment
Date
ID | Item_Code | Comment | Date
1 | B | Done | 2014/08/08
2 | A | Nice A | 2014/08/08
3 | B | Send 1 More | 2014/08/09
4 | C | Done | 2014/08/10
5 | D | This is an A | 2014/08/10
6 | D | Opps Sorry | 2014/08/11
The wanted result: I'm looking to join the most recent comment from Item_Comments to the Items Table
ID | Item_Code | Logged_by | Description | Comment
1 | A | Pete | just an A | Nice A
2 | B | Mary | Seams like a B | Send 1 More
3 | C | Joe | Obviously this is a C | Done
4 | D | Pete | This is another A | Opps Sorry
I did this query but I'm getting all the information =( mixed.
SELECT *
FROM Items t1
JOIN
(SELECT Item_Code, Comment, MAX(date) as MyDate
FROM Item_Comments
Group By Item_Code, Comment, Date
) t2
ON Item_Code= Item_Code
ORDER BY t1.Item_Code;
Do you know any way to do this ?
Try:
select x.*, z.comment
from items x
join (select item_code, max(date) as latest_dt
from item_comments
group by item_code) y
on x.item_code = y.item_code
join item_comments z
on y.item_code = z.item_code
and y.latest_dt = z.date
Fiddle test: http://sqlfiddle.com/#!6/d387f/8/0
You were close with your query but in your inline view aliased as t2 you were grouping by comment, leaving the max function to not actually aggregate anything at all. In t2 you should have just selected item_code and max(date) and grouped only by item_code, then you can use that to join into item_comments (y and z in my query above).
This is a second way of doing this using a subquery, however I would stick to the above (a join w/ an inline view):
select i.*, c.comment
from items i
join item_comments c
on i.item_code = c.item_code
where c.date = (select max(x.date)
from item_comments x
where x.item_code = c.item_code)
order by i.id
Fiddle test: http://sqlfiddle.com/#!6/d387f/11/0
Note if you run this inside piece you get every single record:
SELECT Item_Code, Comment, MAX(date) as MyDate
FROM Item_Comments
Group By Item_Code, Comment, Date
You want only the most recent comment. Assuming this is SQL Server 2008 or earlier, this get's you the most recent date for each Item_Code:
SELECT Item_Code, MAX(date) as MyDate
FROM Item_Comments
Group By Item_Code
Now you need to join that back and look up the comment on that date:
SELECT C.*
FROM Item_Comments C
INNER JOIN
(SELECT Item_Code, MAX(date) as MyDate
FROM Item_Comments
Group By Item_Code
) t2
ON C.Item_Code= t2.Item_Code
AND C.date = t2.MyDate
Now you can use that to join back to your original table:
SELECT t1.*, LatestComment.*
FROM Items t1
INNER JOIN
(
SELECT C.*
FROM Item_Comments C
INNER JOIN
(SELECT Item_Code, MAX(date) as MyDate
FROM Item_Comments
Group By Item_Code
) t2
ON C.Item_Code= t2.Item_Code
AND C.date = t2.MyDate
) LatestComment
On LatestComment.Item_Code = t1.Item_Code
Depending on the actual database you are using, this can get much simpler. Thats why you need to tag your database and version.
Try this,
create table items (id int, item_code char(1), logged_by varchar(10), description varchar(30));
insert into items values (1, 'A', 'Pete', 'just an A');
insert into items values (2, 'B', 'Mary', 'Seams like a B');
insert into items values (3, 'C', 'Joe', 'Obviously this is a C');
insert into items values (4, 'D', 'Pete', 'This is another A');
create table item_comments (id int, item_code char(1), comment varchar(20), date date);
insert into item_comments values (1, 'B', 'Done', '2014/08/08');
insert into item_comments values (2, 'A', 'Nice A', '2014/08/08');
insert into item_comments values (3, 'B', 'Send 1 More', '2014/08/09');
insert into item_comments values (4, 'C', 'Done', '2014/08/10');
insert into item_comments values (5, 'D', 'This is an A', '2014/08/10');
insert into item_comments values (6, 'D', 'Opps Sorry', '2014/08/11');
select * from items;
select * from item_comments;
select * from (select i.logged_by,i.id,i.item_code,i.description,ic.comment
,row_number() over(partition by i.id order by i.id )as Rnk
from items i inner join item_comments ic
on i.item_code=ic.item_code and i.id in(1,3)) x
where x.Rnk=1
union
select * from (select i.logged_by,i.id,i.item_code,i.description,ic.comment
,row_number() over(partition by i.id order by i.id )as Rnk
from items i inner join item_comments ic
on i.item_code=ic.item_code and i.id in(2,4)
) x where x.Rnk=2 order by item_code