I'm not sure what I'm missing but I've been thrashing at this one all afternoon.
I have a Sql Server view of Companies which looks like this:
CompanyId varchar(30) NOT NULL
CompanyName varchar(100) NOT NULL
CompanyPriority int NULL
ConfigItem int NOT NULL
With data that looks a bit like this:
00001 | Company One | 99 | 123
00001 | Company One | 99 | 456
00001 | Company One | 99 | 789
00002 | Company Two | 99 | 123
00002 | Company Two | 99 | 456
I'm trying to get a distinct list of companies. The sql query I want to exectute is
select distinct CompanyId, CompanyName,CompanyPriority from vwCompany
which gives me exactly the results I want which would be
00001 | Company One | 99
00002 | Company Two | 99
But for the life of me I can't find the LINQ query that results in this sql, or anything that produces the same results.
All of the questions I've found use grouping which works fine in my unit tests but fails to return distinct results when executed against an actual database.
EDIT:
So I've tried a few things based on the answers so far.
Dim data = _miRepository.GetCompanies().
Select(Function(c) New With {
.companyId = c.CompanyId,
.companyName = c.CompanyName,
.companyPriority = c.CompanyPriority
}
).Distinct().ToList()
generates the sql
SELECT
1 AS [C1],
[Extent1].[CompanyId] AS [CompanyId],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[CompanyPriority] AS [CompanyPriority]
FROM (SELECT
[vwCompany].[CompanyId] AS [CompanyId],
[vwCompany].[CompanyName] AS [CompanyName],
[vwCompany].[CompanyPriority] AS [CompanyPriority],
[vwCompany].[CiId] AS [CiId]
FROM [dbo].[vwCompany] AS [vwCompany]) AS [Extent1]
which doesn't have the distinct operator in it at all :(
And yes, I'm doing this in VB.NET just to make it harder to find good examples :\
EDIT 2:
I'm trying to get as close to Eric Js answer as I can in VB.
Dim data = (From c In _miRepository.GetCompanies()
Select New With {.companyId = c.CompanyId,
.companyName = c.CompanyName,
.companyPriority = c.CompanyPriority
}
).Distinct().ToList()
gives me
SELECT
1 AS [C1],
[Extent1].[CompanyId] AS [CompanyId],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[CompanyPriority] AS [CompanyPriority]
FROM (SELECT
[vwCompany].[CompanyId] AS [CompanyId],
[vwCompany].[CompanyName] AS [CompanyName],
[vwCompany].[CompanyPriority] AS [CompanyPriority],
[vwCompany].[CiId] AS [CiId]
FROM [dbo].[vwCompany] AS [vwCompany]) AS [Extent1]
Still no distinct keyword to be found :(
Maybe there's a subtle difference in VB.NET that I'm missing.
EDIT 3:
In order to progress with the rest of this application I've given up for the moment and created a new view (vwDistinctCompanies) using the sql statement at the start of the question.
If anyone manages to get this working in VB.NET against a Sql view please let me know. Quite why this should be so difficult in LINQ I have no idea :(
Try using .Distinct() at the end of your query, e.g.
(from r in ctx.MyTable where SomeCondition select r).Distinct();
http://msdn.microsoft.com/en-us/library/system.linq.enumerable.distinct.aspx
If needed, you can also provide an IEqualityComparer. However, to use the IEqualityComparer, the resulting enumerable must first be materialized using .ToEnumerable(). Doing this means the Distinct() operation is performed on the client rather than on the DB server.
http://msdn.microsoft.com/en-us/library/bb338049.aspx
The IEqualityComparer allows you to control exactly which records are treated as equal and therefore not distinct from other records.
If you want to select only a subset of columns of your table, change
select r
to select either an anonymous type like this:
(from r in ctx.MyTable where SomeCondition
select new
{
CompanyId = r.CompanyId,
CompanyName = r.CompanyName,
CompanyPriority = r.CompanyPriority
}
).Distinct();
or if you need a strongly typed object (for example, because you are using strongly typed views in MVC:
public class CompanyModel
{
public int CompanyId { get; set; }
public string CompanyName { get; set; }
public int CompanyPriority { get; set; }
}
// Then in the Linq statement
(from r in ctx.MyTable where SomeCondition
select new CompanyModel()
{
CompanyId = r.CompanyId,
CompanyName = r.CompanyName,
CompanyPriority = r.CompanyPriority
}
).Distinct();
-EDITED:-
Ignore all my code that I mentioned earlier (everything after the end edit section). I tried further test. Plug the following VB code and tell me what results you got:
(From c In ctx.Companies Select New With { _
Key .companyId = c.CompanyId, _
Key .companyName = c.CompanyName, _
Key .companyPriority = c.CompanyPriority _
}).Distinct()
I tested them using LINQPad and I got the following SQL:
SELECT DISTINCT [t0].[CompanyId] AS [companyId],
[t0].[CompanyName] AS [companyName],
[t0].[CompanyPriority] AS [companyPriority]
FROM [Companies] AS [t0]
-END EDIT-
I had this problem few days ago. This is what I ended up doing.
What you are looking for would require the GroupBy clause as you mentioned in your post. Just using Distinct will not work as how you would expect (As far as I know). The following lambda expression is what I did and right after is the SQL statement that is generated through the lambda code.
Lambda Code:
Companies.GroupBy(c => new {
c.CompanyId,
c.CompanyName,
c.CompanyPriority
})
.Select(p => new {
CompanyId = p.Key.CompanyId,
CompanyName = p.Key.CompanyName,
CompanyPriority = p.Key.CompanyPriority
})
SQL Code:
SELECT [t0].[CompanyId] AS [companyId],
[t0].[CompanyName] AS [companyName],
[t0].[CompanyPriority] AS [companyPriority]
FROM [Companies] AS [t0]
GROUP BY [t0].[CompanyId],
[t0].[CompanyName],
[t0].[CompanyPriority]
Try:
var results = (from company in context.Companies
select new {
CompanyId = company.CompanyId,
CompanyName = company.CompanyName,
CompanyPriority = company.CompanyPriority
}).Distinct();
The key is to get the pieces of the query that CAN be unique together, then call the .Distinct() extension. If you leave the configID property in there, they will all be unique.
var query = from c in context.vwCompany
group c by new {
c.CompanyId,
c.CompanyName,
c.CompanyPriority } into g
select g.Key;
Generated query (from SQL Server Profiler) will look like:
SELECT
1 AS [C1],
[Distinct1].[CompanyId] AS [CompanyId],
[Distinct1].[CompanyName] AS [CompanyName]
[Distinct1].[CompanyPriority] AS [CompanyPriority]
FROM ( SELECT DISTINCT
[Extent1].[CompanyId] AS [CompanyId],
[Extent1].[CompanyName] AS [CompanyName]
[Extent1].[CompanyPriority] AS [CompanyPriority]
FROM [dbo].[vwCompany] AS [Extent1]
) AS [Distinct1]
Shouldn't that be two tables? One with
00001 | Company One | 99
00002 | Company Two | 99
And the other with
00001 | 123
00001 | 456
00001 | 789
00002 | 123
00002 | 456
which is more normalized and would make your query really easy?
Related
I wanted to add the count result of more than one queries that belong to different tables.
I am using the below problem as a reference to my actual problem because this problem has already a solution (How do I add two count(*) results together on two different tables?) but I am facing problem in implementing the solution in laravel.
I have two tables: Toys and Games.
+--------------------+------------------+
| Field | Type |
+--------------------+------------------+
| toy_id | int(10) unsigned |
| little_kid_id | int(10) unsigned |
+--------------------+------------------+
+--------------------+------------------+
| Field | Type |
+--------------------+------------------+
| game_id | int(10) unsigned |
| little_kid_id | int(10) unsigned |
+--------------------+------------------+
A little kid can have multiple toys. A little kid can be participating in multiple games at once.
I want a query that will give me the total number of toys + games that a little_kid is involved with.
Basically, I want the sum of these two queries:
SELECT COUNT(*) FROM Toys WHERE little_kid_id = 900;
SELECT COUNT(*) from Games WHERE little_kid_id = 900
The above problem has the following accepted answer
SELECT
(SELECT COUNT(*) FROM Toys WHERE little_kid_id = 900)+
(SELECT COUNT(*) from Games WHERE little_kid_id = 900)
AS SumCount
I wanted to implement the above solution in Laravel.
I have tried the following method but to no avail. It gives syntax error.
$sub=DB::tabel('toys')->select(DB::raw('count(*) as total'))
->where('little_kid_id',$id);
$sub1=DB::tabel('games')->select(DB::raw('count(*) as total'))
->where('little_kid_id',$id);
$result = DB::table( DB::raw("( ({$sub->toSql()})+({$sub1->toSql()}) ) as
total_count") )
->mergeBindings($sub)
->mergeBindings($sub1)
->select('total_count.total')
->get();
I have also tried this method. It works but gives collection but I need an integer value of total count
$result=$sub->unionAll($sub1);
dd($result->get());
In short I wanted to perform the accepted solution of that problem (How do I add two count(*) results together on two different tables?) in laravel.
You can use those codes :
$q = DB::tabel('toys')->select('toy_id','little_kid_id')->where('little_kid_id',900);
$q1 = DB::tabel('games')->select('game_id','little_kid_id')->where('little_kid_id',900)
$data = $q->union($q1)->count();
Don't forget Union require the tables must have the same columns so that I select the columns if your columns will not match each other then don't touch the select statement otherwise feel free to remove the codes
The second way is :
DB::table(DB::raw('(SELECT COUNT(*) as c FROM Toys WHERE little_kid_id = 900) t,(SELECT COUNT(*) as c1 from Games WHERE
little_kid_id = 900) t2'))->selectRaw('t.c+t2.c1 as
SumCount')->toSql(); //change toSql() to get() if you want to get
datas instead of sql code
You can try this. for more information look at https://laravel.com/docs/5.8/queries#raw-expressions. if you want to get request parameters by any user then be confirmed to prevent SQL Injection
I'm a little stuck here. I'm trying to modify a returned View based on a condition. I'm fairly green on SQL and am having a bit of difficultly with the returned result. Heres a partial component of the view I wrote:
WITH A AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY fkidContract,fkidTemplateItem ORDER BY bStdActive DESC, dtdateplanned ASC) AS RANK,
tblWorkItems.fkidContract AS ContractNo,
....
FROM tblWorkItems
WHERE fkidTemplateItem IN
(2895,2905,2915,2907,2908,
2909,3047,2930,2923,2969,
2968,2919,2935,2936,2927,
2970,2979)
AND ...
)
SELECT * FROM A WHERE RANK = 1
The return result is similar to the following:
ContractNo| ItemNumber | Planned | Complete
001 | 100 | 01/01/1900 | 02/01/1900
001 | 101 | 03/04/1900 | 02/01/1901
001 | 102 | 03/06/1901 | 02/08/1900
002 | 100 | 01/03/1911 | 02/08/1913
This gives me the results I expect, but due a nightmare crystal report I need to alter this view slightly. I want to take this returned result set and modify an existing column with a value pulled from the same table and the same Contract relationship, something like the following:
UPDATE A
SET A.Completed = ( SELECT R.Completed
FROM myTable R
INNER JOIN A
ON A.ContractNo = R.ContractNo
WHERE A.ItemNumber = 100 AND R.ItemNumber = 101
)
What I'm trying to do is modify the "Completed Date" of one task and make it the complete date of another task if they both share the same ContractNo field value.
I'm not sure about the ItemNumber relationships between A and R (perhaps it was just for testing...), but it seems like you don't really want to UPDATE anything, but you want to use a different value under some circumstances. So, maybe you just want to change the non-cte part of your query to something like:
SELECT A.ContractNo, A.ItemNumber, A.Planned,
COALESCE(R.Completed,A.Completed) as Completed
FROM A
LEFT OUTER JOIN myTable R
ON A.ContractNo = R.ContractNo
AND A.ItemNumber = 100 AND R.ItemNumber = 101 -- I'm not sure about this part
WHERE A.Rank = 1
So it turns out that actually reading the vendor documentation helps :)
SELECT
column1,
column2 =
case
when date > 1999 then 'some value'
when date < 1999 then 'other value'
else 'back to the future'
end
FROM ....
For reference, the total query did a triple inner join over ~5 million records and this case statement was surprisingly performant.
I suggest that this gets closed as a duplicate.
I'm trying to select all Projects which have Employees who are AtWork.
Projects:
ProjName | EmpOnProj
--------------------------
Alpha | 1, 2, 3
Beta | 1, 3
Employees:
EmpID | EmpName | AtWork
-------------------------------------
1 | John | TRUE
2 | Mark | FALSE
3 | Mary | TRUE
I need to output all projects which could currently be worked on; ie, I need to show Beta because the employees working on Beta are at work.
Currently I cannot say "ALL EMPLOYEES MUST BE AT WORK" only the following:
SELECT ProjName FROM Projects INNER JOIN
Employees ON EmpOnProj.Value = EmpID
WHERE AtWork = true
GROUP BY ProjName
which returns both, as it sees one correct employee and displays the project.
I think I solved this one. Basically I'm saying 'show all projects except those where somebody is NOT at work'
http://sqlfiddle.com/#!3/36c48/2
SELECT DISTINCT
p_global.ProjName
FROM
Projects AS p_global
WHERE
p_global.ProjName NOT IN
(SELECT DISTINCT
p1.ProjName
FROM
Projects p1 INNER JOIN Employees AS e ON p1.EmpOnProj = e.EmpID
WHERE
e.AtWork = 0)
There may be a simpler solution but this works (or it looks like it anyway) :)
Edit: Modified to remove GROUP BYs as suggested in comments.
If can't really answer your question but this stuff can guide you through simplification and can help you solve you question. Currently your table is not in good format. Instead of having comma separated values, why not do it in rows instead? Like this,
Projects:
ProjName | EmpOnProj
--------------------------
Alpha | 1
Alpha | 2
Alpha | 3
Beta | 1
Beta | 3
In this way you can easily join both tables. Example
SELECT a.EmpID, a.EmpName,
iif (ISNULL(b.EmpOnProj), 'False', 'True') AtWork
FROM Employees a
LEFT JOIN Projects b
ON a.EmpID = b.EmpOnProj
WHERE b.ProjName = 'Beta'
Let's assume you move beyond the EmpOnProj column, which, as implemented in your example, violates first normal form, and replace it with an associative entity called ProjEmp, whose primary key is (ProjName, EmpID)
SELECT p.ProjName FROM Projects p
LEFT OUTER JOIN
(SELECT eop.ProjName FROM ProjEmp eop
INNER JOIN JOIN Employees e
ON e.EmpId = eop.EmpId
AND e.AtWork = FALSE
) AS empNotHere
ON empNotHere.ProjName = p.ProjName
WHERE empNotHere.ProjName IS NULL
;
I'm assuming I misunderstood your question since your SQL seems to contradict the schema you provided. But if your table is formatted the way you listed, you have to jump through hoops. Here's a solution that involves making a UDF, to avoid extremely complicated SQL.
Add this to a module:
Function WhoIsAtWork() As String
Dim rs As Recordset
Set rs = CurrentDb.OpenRecordset("Select * from Employees where AtWork = true Order by EmpID")
Do While Not rs.EOF
WhoIsAtWork = WhoIsAtWork & rs!EmpID & ", "
rs.MoveNext
Loop
If Len(WhoIsAtWork) <> 0 Then
WhoIsAtWork = Left(WhoIsAtWork, Len(WhoIsAtWork) - 2)
End If
rs.Close
Set rs = Nothing
End Function
Then your SQL would be this:
SELECT ProjName
FROM Projects
WHERE Projects.EmpOnProj=WhoIsAtWork();
SELECT * INTO #Loc1
FROM LocaleStringResource
Where LanguageId = 1
SELECT * INTO #Loc2
FROM LocaleStringResource
Where LanguageId = 3
SELECT Loc1.Id, Loc1.LanguageId, Loc1.ResourceName, Loc1.ResourceValue,
Loc2.Id, Loc2.LanguageId, Loc2.ResourceName, Loc2.ResourceValue
FROM #Loc1 AS Loc1 INNER JOIN
#Loc2 AS Loc2 ON Loc1.ResourceName = Loc2.ResourceName
Update:
I have a table named - LocaleStringResource.
Columns: Id, LanguageId, ResourceName, ResourceValue,
Suppose I have 2 language in my system.
Id Language
1 English
3 Bangla
Id LanguageId ResourceName ResourceValue
1 1 Admin.Address Address
2 1 Admin.Phone Phone
51 3 Admin.Address SpAddress
51 3 Admin.Phone SpPhone
By ResourceName i get what the value is for choosen language. So from admin page i want to see for by ResourceName what are ResourceValue for both languages.
So I need a resultset which returns something like this.
ResourceName ResourceValue As EnglishText ResourceValue As SpanishText
Admin.Address Address SpAddress
Admin.Phone Phone SpPhone
var loc1 = Context.ObjectSet<LocaleStringResource>.Where(r => r.LanguageId == 1);
var loc2 = Context.ObjectSet<LocaleStringResource>.Where(r => r.LanguageId == 2);
var result = (
from l1 in loc1
join l2 in loc2 on l1.ResourceName equals l2.ResourceName
select new{
ResourceName = l1.ResourceName,
EnglishText = l1.ResourceValue,
SpanishText = l2.ResourceValue
}
).ToList();
Entity framework doesn't support temporary tables and SELECT INTO construction so you cannot directly convert it. You must go through the logic of your application and change it so that it can be used with EF or you must wrap this code into stored procedure and call it from EF (btw. you will have to use column aliases for result set because column names must not be the same when used with EF).
I am working on a SQL Statement that I can't seem to figure out. I need to order the results alphabetically, however, I need "children" to come right after their "parent" in the order. Below is a simple example of the table and data I'm working with. All non relevant columns have been removed. I'm using SQL Server 2005. Is there an easy way to do this?
tblCats
=======
idCat | fldCatName | idParent
--------------------------------------
1 | Some Category | null
2 | A Category | null
3 | Top Category | null
4 | A Sub Cat | 1
5 | Sub Cat1 | 1
6 | Another Cat | 2
7 | Last Cat | 3
8 | Sub Sub Cat | 5
Results of Sql Statement:
A Category
Another Cat
Some Category
A Sub Cat1
Sub Cat 1
Sub Sub Cat
Top Category
Last Cat
(The prefixed spaces in the result are just to add in understanding of the results, I don't want the prefixed spaces in my sql result. The result only needs to be in this order.)
You can do it with a hierarchical query, as below.
It looks a lot more complicated than it is, due to the lack of a PAD funciton in t-sql. The seed of the hierarchy are the categories without parents. The fourth column we select is their ranking alphabetically (converted to a string and padded). Then we union this with their children. At each recursion, the children will all be at the same level, so we can get their ranking alphabetically without needing to partition. We can concatenate these rankings together down the tree, and order by that.
;WITH Hierarchy AS (
SELECT
idCat, fldCatName, idParent,
CAST(RIGHT('00000'+
CAST(ROW_NUMBER() OVER (ORDER BY fldCatName) AS varchar(8))
, 5)
AS varchar(256)) AS strPath
FROM Category
WHERE idParent IS NULL
UNION ALL
SELECT
c.idCat, c.fldCatName, c.idParent,
CAST(h.strPath +
CAST(RIGHT('00000'+
CAST(ROW_NUMBER() OVER (ORDER BY c.fldCatName) AS varchar(8))
, 5) AS varchar(16))
AS varchar(256))
FROM Hierarchy h
INNER JOIN Category c ON c.idParent = h.idCat
)
SELECT idCat, fldCatName, idParent, strPath
FROM Hierarchy
ORDER BY strPath
With your data:
idCat fldCatName idParent strPath
------------------------------------------------
2 A Category NULL 00001
6 Another Category 2 0000100001
1 Some Category NULL 00002
4 A Sub Category 1 0000200001
5 Sub Cat1 1 0000200002
8 Sub Sub Category 5 000020000200001
3 Top Category NULL 00003
7 Last Category 3 0000300001
It can be done in CTE... Is this what you're after ?
With MyCats (CatName, CatId, CatLevel, SortValue)
As
( Select fldCatName CatName, idCat CatId,
0 Level, Cast(fldCatName As varChar(200)) SortValue
From tblCats
Where idParent Is Null
Union All
Select c.fldCatName CatName, c.idCat CatID,
CatLevel + 1 CatLevel,
Cast(SortValue + '\' + fldCatName as varChar(200)) SortValue
From tblCats c Join MyCats p
On p.idCat = c.idParent)
Select CatName, CatId, CatLevel, SortValue
From MyCats
Order By SortValue
EDIT: (thx to Pauls' comment below)
If 200 characters is not enough to hold the longest concatenated string "path", then change the value to as high as is needed... you can make it as high as 8000
I'm not aware of any SQL Server (or Ansi-SQL) inherent support for this.
I don't supposed you'd consider a temp table and recursive stored procedure an "easy" way ? J
Paul's answer is excellent, but I thought I would throw in another idea for you. Joe Celko has a solution for this in his SQL for Smarties book (chapter 29). It involves maintaining a separate table containing the hierarchy info. Inserts, updates, and deletes are a little complicated, but selects are very fast.
Sorry I don't have a link or any code to post, but if you have access to this book, you may find this helpful.