I got two tables with data as listed below:
Table1: Student
Table2: Subject
I need the output as:
I got this acheived with below query using for XML PATH
Code:
WITH cte
AS ( SELECT Stu.Student_Id ,
Stu.Student_Name ,
( SELECT Sub.[Subject] + ','
FROM [Subject] AS Sub
WHERE Sub.Student_Id = Stu.Student_Id
ORDER BY Sub.[Subject]
FOR
XML PATH('')
) AS [Subjects]
FROM dbo.Student AS Stu
)
SELECT Student_id [Student Id] ,
student_name [Student Name] ,
SUBSTRING(Subjects, 1, ( LEN(Subjects) - 1 )) AS [Student Subjects]
FROM cte
My question is there a better way to do this without using XML Path?
This is a very good approach and has become pretty well accepted. There are several approaches and this blog post describes a lot of them.
One interesting approach that exists is using the CLR to do the work for you which will significantly reduce the complexity of the query with the trade-off of running external code. Here is a sample of what the class might look like in the assembly.
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Server;
[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize=8000)]
public struct strconcat : IBinarySerialize{
private List values;
public void Init() {
this.values = new List();
}
public void Accumulate(SqlString value) {
this.values.Add(value.Value);
}
public void Merge(strconcat value) {
this.values.AddRange(value.values.ToArray());
}
public SqlString Terminate() {
return new SqlString(string.Join(", ", this.values.ToArray()));
}
public void Read(BinaryReader r) {
int itemCount = r.ReadInt32();
this.values = new List(itemCount);
for (int i = 0; i <= itemCount - 1; i++) {
this.values.Add(r.ReadString());
}
}
public void Write(BinaryWriter w) {
w.Write(this.values.Count);
foreach (string s in this.values) {
w.Write(s);
}
}
}
And that would net a query a bit more like this.
SELECT CategoryId,
dbo.strconcat(ProductName)
FROM Products
GROUP BY CategoryId ;
Which is quite a bit simpler obviously. Take it for what it's worth :)
Good day!
Related
I'm having a problem of defining a data type for a list of grouped rows in LINQ to SQL.
I have a model MyDBModel with 2 properties
public class MyDBModel
{
public List<a_Cases_tbl> a_Cases { get; set; }
public List<b_Cases_tbl> b_Cases { get; set; }
}
In a controller I try to get data into this properties
MyDBDataContext myDb = new MyDBDataContext();
var listModel = new MyDBModel();
listModel.a_Cases=
(from t in myDb.a_cases_table
group t by new { t.Date, t.Name, t.Category, t.Code }
into myGroup
select new { myGroup.Key.Date, myGroup.Key.Name, myGroup.Key.Category, myGroup.Key.Code });
listModel.b_Cases =
(from t in myDb.B_cases_table
group t by new { t.Date, t.Name, t.Category, t.Code }
into myGroup
select new { myGroup.Key.Date, myGroup.Key.Name, myGroup.Key.Category, myGroup.Key.Code });
I get error:
Cannot implicitly convert type 'System.Collections.Generic.List < AnonymousType#1 > ' to 'System.Collections.Generic.List < MVC4.a_Cases_tbl > '
What will Data type for a_Cases should be if the result is going to be a List of Grouped by Multiple columns? Or how to define List < IGrouping < Tkey, Telement > > if the result is grouped by multiple fields of different types?
In your code you have created the list of anonymous type. You must tell compiler that you create the objects of appropriate type.
select new a_Cases_tbl{ myGroup.Key.Date, myGroup.Key.Name, myGroup.Key.Category, myGroup.Key.Code });
....
select new b_Cases_tbl{ myGroup.Key.Date, myGroup.Key.Name, myGroup.Key.Category, myGroup.Key.Code });
I recently started experimenting with Raw SQL using Entity Framework, and I prefer it in some cases.
I would like to know if there is a way to grab relational entities:
Dosnt work:
var personNo = context.Person.SqlQuery("SELECT * FROM `Person` LIMIT 1").FirstOrDefault();
foreach(var Number in personNo.Phone) {
//never iterates
}
Works (but LINQ-to-Entities which I dont want in this case):
var personNo = context.Person.FirstOrDefault();
foreach(var Number in personNo.Phone) {
//iterate twice as it contains in the db
}
I tried a couple of different queries including
SELECT * FROM `Person` LEFT JOIN `Phone` ON (`Person`.ID = `Phone`.PersonID) LIMIT 1
What is the correct way to write the query on to recieve the list of Phone numbers? Is this possible?
You can do SQL to entities. I prefer this way when I have joins and group bys in them, and don't have permission to create a view.
First, create a class and add properties with the same names as the returned columns.
public class PersonWithAddress
{
public int Id { get; set; }
public String Name { get; set; }
public String Address { get; set; }
}
And the C# with parameters
MyEntity db = new MyEntity();
String sqlQuery = #"
SELECT Id, Name, Address
FROM Person p
JOIN Address a
ON a.Id = p.Id
WHERE Name = #Name
AND Address = #Address
";
String searchName = "Joe";
String address = "123 Something Lane";
DbRawSqlQuery<PersonWithAddress> results = db.Database.SqlQuery<PersonWithAddress>(
sqlQuery,
new SqlParameter("#Name", searchName),
new SqlParameter("#Address", address)
);
foreach(PersonWithAddress a in results)
{
}
You can list as many parameters as you want.
Wow this question's 2 years old. I didn't even realize that.
In Oracle it works well ......
Query for oracle is As Follows
Select distinct channel_id, position_id,datamonth,
percentile_cont(.9) within group (order by TRIM_PRE_ELIG_PAY)
over (partition by channel_id, position_id, datamonth) as TRIM_PRE_ELIG_PAY_90th_PERC
from Tablename
But for SQL Server, I'm getting an error. Here's the query for SQL Server 2008:
Select
distinct channel_id,
position_id, datamonth,
percentile_cont(.9) within group (order by TRIM_PRE_ELIG_PAY)
over (partition by channel_id) as TRIM_PRE_ELIG_PAY_90th_PERC
from table
ERROR: Select could not be parsed correctly. Output could not be
generated.
I got to know that it can work properly in SQL Server 2012 but need an alternative way in SQL Server 2008
Can anybody help...........
There is a workaround on the SQL Server Engine blog that applies to SQL Server 2005+
Unfortunately, it's quite long and convoluted: I'll leave you with the link rather than attempt to adapt it for your query...
You can create a CLR Aggregate function to implement the same. The only downfall is that you will have to re-arrange your query a bit. I implemented the percentile_cont using the CLR. Read here on how to create the CLR . Then you can use this code to get the same O/P as percentile_cont. Its a lot more easier than writing multiple statements.
You can definitely refine/tweak it a bit depending on your usage.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Collections.Generic;
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
Format.UserDefined,
IsInvariantToDuplicates = false,
IsInvariantToNulls = false,
IsInvariantToOrder = false,
MaxByteSize = 8000)]
public struct Percentile_Cont : IBinarySerialize
{
//Variables to hold the values;
private List<decimal> _list;
private decimal _percentile;
public void Init()
{
_list = new List<decimal>();
_percentile = new decimal();
}
public void Accumulate(SqlDecimal value,SqlDecimal percentile)
{
if (!value.IsNull)
{
_list.Add(value.Value);
_percentile = (decimal)percentile;
}
}
///
/// Merge the partially computed aggregate with this aggregate.
///
/// The other partial results to be merged
public void Merge(Percentile_Cont group)
{
this._list.AddRange(group._list.ToArray());
}
///
/// Called at the end of aggregation, to return the results.
///
/// The percentile of all inputted values
public SqlDecimal Terminate()
{
if (_list.Count == 0)
return SqlDecimal.Null;
_list.Sort();
if (_percentile < 0 || _percentile >= 1)
return SqlDecimal.Null;
var index =
(int) Math.Ceiling
(_percentile * _list.Count + 0.5m);
if(index > _list.Count)
{
index = index - 1;
}
return _list[index-1];
}
#region IBinarySerialize Members
public void Read(System.IO.BinaryReader binaryReader)
{
int cnt = binaryReader.ReadInt32();
this._list = new List<decimal>(cnt);
this._percentile = new decimal();
for (int i = 0; i < cnt; i++)
{
this._list.Add(binaryReader.ReadDecimal());
}
this._percentile = binaryReader.ReadDecimal();
}
public void Write(System.IO.BinaryWriter binaryWriter)
{
binaryWriter.Write(this._list.Count);
foreach (decimal d in this._list)
{
binaryWriter.Write(d);
}
binaryWriter.Write(_percentile);
}
#endregion
}
I have been building a Motion Picture application to manage actors or "Talents". I have a TALENT table and a LANGUAGES table. I also have a TALENTLANGUAGES table that shows the many to many relationship between the two.
Here is the SQL i can write to show the different languages a given talent speaks.
Select t.TalentID, t.FirstName, tl.LanguageID, l.Name from Talent t
inner join TalentLanguage tl on tl.TalentID = t.TalentID
inner join Language l on l.LanguageID = tl.LanguageID
where t.TalentID = 10000;
Im in my C# application I'm using Linq to sql classes. How might I do the above code with linq to sql. Thanks.
Here's one way you can do it:
Start by creating a "results" object, something that will hold the information that you need in one object. Let's call it "TalentLanguagesContainer"
public class TalentLanguagesContainer
{
public int TalentID { get; set; }
public string FirstName { get; set; }
public int LanguageID { get; set; }
public string LanguageName { get; set; }
}
Then, create a Select statement that will map your needs appropriately:
public IQueryable < TalentLanguagesContainer > GetTalentLanguages()
{
MyDataContext _dataContext = new MyDataContext();
return _dataContext.TalentLanguages
.Where(t => t.TalentID == 10000)
.Select(tl => new TalentLanguagesContainer() {
TalentID = tl.TalentID,
FirstName = tl.Talent.FirstName,
LanguageID = tl.LanguageID,
LanguageName = tl.Language.Name });
}
Also, you may want to consider writing stored procedures for more complex scripts such as this one - you may find an SQL script to perform faster too.
Remus, I think I'm gonna answer this myself because it's such a clean solution. Check this out...
var languages = from tl in talentDB.TalentLanguages
where tl.TalentID == id
select new { lang = tl.Language.Name, tal_id = tl.TalentID }; // could get more values if needed..
foreach (var l in languages)
{
string language = l.lang;
string talentID = l.tal_id;
// etc...
}
This is pretty cool! Linq did the join for me!!
I have multiple queries that I'd like to union together, then compile the entire thing. The uncompiled query runs fine, but an "InvalidOperationException: Member access 'Int32 Id' of 'UserQuery+Foo' not legal on type 'System.Linq.IQueryable`1[UserQuery+Foo]." exception is thrown when the same query is compiled and run.
How do I fix this?
void Main()
{
var db = new MyDataContext( "..." );
Expression < Func < DataContext, int, IQueryable < Foo > > > queryExpression = (DataContext dc, int unused) =>
from ab in GetA(dc).Union( GetB(dc) )
group ab by new { ab.Id, ab.Name } into grp
select new Foo
{
Id = grp.Key.Id,
Name = grp.Key.Name,
Total = grp.Count()
};
var final = CompiledQuery.Compile ( queryExpression );
var result1 = queryExpression.Compile () (db, 0); // calling the original query works fine
var result2 = final (db, 0); // calling the compiled query throws an exception
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public int Total { get; set; }
}
IQueryable<Foo> GetA( DataContext db )
{
return from b in db.GetTable<Bar>()
where b.IsActive
select new Foo { Id = b.Id, Name = b.Name };
}
IQueryable<Foo> GetB( DataContext db )
{
return from b in db.GetTable<Bar>()
where !b.IsActive
select new Foo { Id = b.Id, Name = b.Name };
}
EDIT
It looks like the union and grouping are irrelevant. Removing those elements from the query still causes an exception when compiled:
Expression < Func < DataContext, int, IQueryable < Foo > > > queryExpression = (DataContext dc, int unused) =>
from a in GetA(dc)
select new Foo
{
Id = a.Id,
Name = a.Name,
Total = 42
};
Replacing the call to GetA(dc) with dc.GetTable<Bar>() and adding the where clause fixes the issue.
So, is connecting separate queries together like this simply not possible for compiled queries?
EDIT #2
James' answer hit the nail on the head. Simplifying the query even further reveals the root problem:
Expression < Func < DataContext, int, IQueryable < Foo > > > queryExpression = (DataContext dc, int unused) =>
from a in GetA(dc)
select a;
This query throws NotSupportedException: Method 'System.Linq.IQueryable``1[UserQuery+Foo] GetA(System.Data.Linq.DataContext)' has no supported translation to SQL.
Pulling the call to GetA out into a separate variable assignment, then using that variable in the query throws a InvalidOperationException: Sequence contains more than one element exception.
I had the same issue and what seemed to do the trick for me was separating out an inline static method call that returned IQueryable<> so that I stored this deferred query into a variable and referenced that.
I think this is a bug in Linq to SQL but at least there is a reasonable workaround.
My guess is that the linq compiler doesn't understand the methods returning IQueryable.
To compile it, those methods would probably have to return some form of Expression<>.