Select Only Non-Matches Using Single Query (Joins Only) - c#

I have a pretty simple many-to-many table structure (see below) in SQL Server (T-SQL).
I need to:
Select all jobs that don't have an associated person
Select all jobs that are only associated with people who are not visible and/or are deleted
Select all jobs that are deleted.
All of this should return a single table of results
Is this possible in a single join-only query (without using IN/NOT IN, nested selects, unions, or temp tables, etc)?
SCHEMA:
Person
ID
IsVisible
IsDeleted
PersonJobs
PersonID
JobID
Jobs
ID
IsDeleted
I should be able to do each query individually, then join the dataset in code, but I was curious if there's simpler way to do this using joins.
Pseudo code is fine in response, if accurate.
Thanks!

Try
select j.*
from personjobs pj
full join jobs j on pj.jobid = j.id
full join person p on pj.personid = p.id
where pj.personid is null
or p.isdeleted = 1
or p.isvisible = 0
or j.isdeleted = 1

CASE statement is used for identification of the given condition of each raw. If you don't need to know them simply remove it.
Select J.Id, CASE WHEN PersonVisible Is Null THEN '1 - Not Associated'
WHEN PersonVisible = 1 OR PersonDeleted = 1 THEN '2 - Person NotVisible/Deleted'
WHEN J.Deleted = 1 THEN '3 - Deleted Jobs'
ELSE '4 - All Except above' END as Conditions
From Jobs J Left Join
(Select jobId,IsVisible PersonVisible, IsDeleted PersonDeleted
From Person p join PersonJobs pj on p.Id = pj.personId
) X On J.Id = X.jobId
Order by Conditions

Related

Multiple AND conditions on the same column [Servicestack.OrmLite]

I was wondering if it's possible to have multiple AND conditions on the same column using Servicestack.OrmLite. This is the SELECT statement I printed out, but It always returns 0. I should get the product count from the products having both specifications with id 1016 and 17.
SELECT COUNT(DISTINCT "Product"."Id")
FROM "Product"
INNER JOIN "ProductManufacturer"
ON ("Product"."Id" = "ProductManufacturer"."ProductId")
INNER JOIN "ProductSpecificationAttribute"
ON ("Product"."Id" = "ProductSpecificationAttribute"."ProductId")
WHERE ("ProductManufacturer"."ManufacturerId" = 6)
AND ("ProductSpecificationAttribute"."SpecificationAttributeOptionId" = 1016)
AND ("ProductSpecificationAttribute"."SpecificationAttributeOptionId" = 17)
A single column value can't possibly have two values at the same time.
What you want is either:
AND
(
ProductSpecificationAttribute.SpecificationAttributeOptionId = 1016
OR
ProductSpecificationAttribute.SpecificationAttributeOptionId = 17
)
Or, more succinctly:
AND
(
ProductSpecificationAttribute.SpecificationAttributeOptionId
IN (1016, 17)
)
And turn off whatever option is forcing your tool to "inject" "double" "quotes" "around" "every" "entity" "name" because it makes the query text unmanageable. You might also consider using aliases and schema prefixes, like INNER JOIN dbo.ProductSpecificationAttribute AS psa...
After further clarification... the goal is to find products where they have both of those attributes on different rows, which isn't clear from the description or the code ORMLite barfed out. Here's what you want in that case (there are several ways to do this, but converting everything to EXISTS also allows you to remove the DISTINCT from the COUNT, which is never free):
SELECT COUNT(Product.Id) FROM dbo.Product AS p
WHERE EXISTS
(
SELECT 1 FROM dbo.ProductManufacturer AS pm
WHERE pm.ProductId = p.Id AND pm.ManufacturerId = 6
)
AND EXISTS
(
SELECT 1 FROM dbo.ProductSpecificationAttribute AS psa
WHERE psa.ProductId = p.Id
AND psa.SpecificationAttributeOptionId = 1016
)
AND EXISTS
(
SELECT 1 FROM dbo.ProductSpecificationAttribute AS psa
WHERE psa.ProductId = p.Id
AND psa.SpecificationAttributeOptionId = 17
);
If ProductSpecificationAttribute is poorly index and this leads to two scans, you could change that by saying something like this (untested, but I'm happy to test it out if you can produce a db<>fiddle:
SELECT COUNT(Product.Id) FROM dbo.Product AS p
WHERE EXISTS
(
SELECT 1 FROM dbo.ProductManufacturer AS pm
WHERE pm.ProductId = p.Id
AND pm.ManufacturerId = 6
)
AND EXISTS
(
SELECT 1 FROM dbo.ProductSpecificationAttribute AS psa
WHERE psa.ProductId = p.Id
AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY psa.ProductId, psa.SpecificationAttributeOptionId
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) > 1
);
It's also really weird that the table ProductManufacturer has a list of ProductIDs in it that point back to Product - usually Product would have a ManufacturerID that points in the other direction.
Anyway, you might consider using stored procedures that your ORM can call if it has problems creating queries beyond basic CRUD (which is unfortunately a limitation of all ORMs to some degree - they're great at the basics, covering 80% of the use case, but they're terrible at the other 20% - unfortunately most of us end up needing that other 20% before too long).
You can get all the product ids that you want if you group by product and set the conditions in the HAVING clause:
SELECT p.Id
FROM Product p
INNER JOIN ProductManufacturer pm ON p.Id = pm.ProductId
INNER JOIN ProductSpecificationAttribute psa ON p.Id = psa.ProductId
WHERE pm.ManufacturerId = 6 AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY p.Id
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) = 2; -- both specifications must exist
If you want to count these products you could either use the above query as a subquery or a cte and count the rows:
WITH cte AS (
SELECT p.Id
FROM Product p
INNER JOIN ProductManufacturer pm ON p.Id = pm.ProductId
INNER JOIN ProductSpecificationAttribute psa ON p.Id = psa.ProductId
WHERE pm.ManufacturerId = 6 AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY p.Id
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) = 2;
)
SELECT COUNT(*) FROM cte;
or, use COUNT() window function:
SELECT DISTINCT COUNT(*) OVER ()
FROM Product p
INNER JOIN ProductManufacturer pm ON p.Id = pm.ProductId
INNER JOIN ProductSpecificationAttribute psa ON p.Id = psa.ProductId
WHERE pm.ManufacturerId = 6 AND psa.SpecificationAttributeOptionId IN (17, 1016)
GROUP BY p.Id
HAVING COUNT(DISTINCT psa.SpecificationAttributeOptionId) = 2;

EF Core/SQL Join a different entity based on a condition

Lets suppose I have a table called Transactions
Transactions has the following columns
OrderId,
OrderType (Can be 0 = Sale or 1 = Purchase) <--- this can increase
Amount
Now I want to get the relevant data based on the OrderType
if OrderType = 0 then join from Sale Table else Join from Purchase Table.
Currently what I am doing is that doing three calls to the database to get the some other values from the other tables(which works but highly inefficient in long run as 3 Calls are bad performance wise).
My solution is using left join with SQL
SELECT ap.*,
coalesce(s.orderNo,p.orderNo) as orderNo
FROM apptransactions AS ap
LEFT JOIN sales AS s ON (ap.orderType = 0 and ap.orderId = s.id)
LEFT JOIN purchases AS p ON (ap.orderType = 1 and ap.orderId = p.id);
how can this query be converted to EF Core?
Why is your query not like this?
var results = context.Transactions.Select(t =>
new
{
/* t.column list, there's no t.* in LINQ */,
OrderNo = t.OrderType == 0 ? t.Sale.OrderNo : t.Purchase.OrderNo
});
Let EF generate any underlying joins it needs to, concern yourself with getting the results you want.
This also alludes to what #caius mentions. Your model is likely not high level enough or incorrectly mapped.

Linq Join variable scoping issue

Really odd issue that I cant work out.
I am trying to join two tables in a linq statement to only retrieve records where the record in table 1 has no related rows in table 2.
I have used Joins before but for some reason I cant get VS to recognise the second table in the linq statement.
EG.
var result =
(from pc in _dataSource.Payments
join bc in _dataSource.BouncedCheques
on pc.PaymentID != bc.PaymentID //This is where the error occurs, VS does not recognise "bc"
where pc.CustomerNumber == getAccountNumber
& pc.IsDeleted == false
orderby pc.PaymentDate descending
select new PaymentAllocation
{
PaymentId = pc.PaymentID,
PaymentDate = pc.PaymentDate,
CustomerNumber = pc.CustomerNumber,
ChequeReference = pc.ChequeReference,
PaymentValue = pc.PaymentValue,
AllocatedValue = pc.AllocatedValue,
UnallocatedValue = pc.PaymentValue - pc.AllocatedValue,
ReceivedBy = pc.ReceivedBy,
PaymentType = pc.PaymentType,
PostedDate = pc.PostedDate
});
Basically the problem is that the variable "bc" does not seem to be recognised, however I have several other similar Linq queries that all work well
Any ideas?
Your problem is that the syntax for join uses the keyword equals and not standard boolean operators.
Try replacing your join by a cartesian product of your tables:
from pc in _dataSource.Payments
from bc in _dataSource.BouncedCheques
where
pc.PaymentID != bc.PaymentID
&& pc.CustomerNumber == getAccountNumber
& pc.IsDeleted == false
In the join clause you should use the equals keyword:
try:
on pc.PaymentID equals bc.PaymentID

NHibernate join on aggregated count of self

Hi I am new to NHibernate and having trouble doing a join on an aggregated set of self. I think showing the SQL I am trying to achieve might help:
The Data Looks Like this:
Message TABLE
ID DIRECTION BATCHID SEQUENCE ISLASTINSEQUENCE
1 Outbound 1 1 0
2 Outbound 1 2 0
3 Outbound 1 3 1
The query looks like this:
--Get all msgs with batchId where full sequence is ready
SELECT *
FROM [Message] M
JOIN (
--Group Msg on batchId and Count Sequence + cross ref using having
SELECT M.BatchId
FROM [Message] M
JOIN (
--Get BatchId of last msg in sequence
SELECT BatchId, Sequence as LastInSequence
FROM [Message]
WHERE Direction = 'Outbound'
AND IsEndOfSequence = 1
) M1 ON M.BatchId = M1.BatchId
WHERE Direction = 'Outbound'
GROUP BY M.BatchId, LastInSequence
HAVING count(1) = M1.LastInSequence
) B ON M.BatchId = B.BatchId
Basically I want to include batches where I have the full sequence
Here is may HNibernate Linq attempt:
var lastInSeqenceMsgs =
from b in Query<Message>(x => x.Direction == MessageDirection.Outbound
&& x.IsEndOfSequence)
select new {b.BatchId, LastInSequence = b.Sequence};
var fullSequenceBatchIds =
from outboundMessage in Query<Message>(x => x.Direction ==
MessageDirection.Outbound)
join lastInSequence in (lastInSeqenceMsgs)
on outboundMessage.BatchId equals lastInSequence.BatchId
group lastInSequence by new {lastInSequence.BatchId, lastInSequence.LastInSequence}
into g
where g.Count() == g.Key.LastInSequence
select g.Key.BatchId;
var allMsgsFromWithCompleteSequences =
from fullSequenceMessage in Query<Message>(x => x.Direction ==
MessageDirection.Outbound)
join test in (fullSequenceBatchIds) on
fullSequenceMessage.BatchId equals test.BatchId
select test;
To which it bombs on the second query (I evaluated the queries - not shown) with the following exception:
Unable to cast object of type 'System.Linq.Expressions.NewExpression' to type 'Remotion.Linq.Clauses.Expressions.QuerySourceReferenceExpression'.
Of which I refined back to the join on self
var fullSequenceBatchIds =
from outboundMessage in Query<Message>(x => x.Direction ==
MessageDirection.Outbound)
join lastInSequence in (lastInSeqenceMsgs)
on outboundMessage.BatchId equals lastInSequence.BatchId
select outboundMessage;
To which I get the exception
"Specified method is not supported."
I am getting brick imprints on my forehead, so some help here would be greatly appreciated.
In general I can just say that the nhibernate Linq provider does NOT support everything what Linq offers.
In addition, nhibernate does NOT support sub queries. It simply doesn't work and you have to work around it somehow.

Get only one (last) record in one-to-many join with linq-to-entities

I have the following in linq-to-entities
clientprojects = (from p in this.SAPMappingEntities.SAP_Master_Projects
join c in this.SAPMappingEntities.SAP_Master_ProjectPartners on c.project_no equals p.project_no
where c.partner_name.Contains(clientstring)
orderby p.start descending
select new ClientProjects { client = c.partner_name, location = c.city +", "+c.region, project_no = c.project_no, start_dt = p.start, end_dt = p.finish }).Take(50).ToList();
I would like change this query so that for each SAP_Master_Project only get the SAP_Master_ProjectPartners record which has the latest update_dt. How can I do this?
EDIT
There's a project table with a project number and project details including project start and end dates. There's a project partners table with the project partner number, name, project number, update date and other details.
SAP_MASTER_PROJECT
project_no
start
finish
SAP_MASTER_PROJECTPARTNERS
partner_no
project_no
partner_name
city
region
update_dt
When the user enters "ABC" into a text box, the info I want to return is the project number, project start date, project end date plus project partner name, city, and state from the last project partner record for the last 50 projects (based on start date) where the project partner name contains or is like "ABC".
I'm sure there's more than one way to do this, but his SQL gives me the results that I need:
SELECT TOP 50 p.project_no, p.start, p.finish, c.partner_name, c.city, c.region
FROM
(select pp.project_no, pp.partner_name, pp.city, pp.region
from SAP_Master_ProjectPartners pp
where pp.partner_name LIKE #clientstring AND pp.update_dt = (select max(pp1.update_dt)
from SAP_Master_ProjectPartners pp1
where pp1.project_no = pp.project_no)) c
join SAP_Master_Projects p
on (p.project_no = c.project_no)
ORDER BY p.start DESC
EDIT #2
That sql actually returns a few items which have the same update_dt, so I modified the sql to below. Still struggling to convert to linq.
SELECT TOP 50 p.project_no, p.start, p.finish, c.partner_name, c.city, c.region, c.update_dt, c.row_id
FROM SAP_Master_Projects p
join
(select pp.project_no, pp.partner_name, pp.city, pp.region, pp.update_dt, pp.row_id
from SAP_Master_ProjectPartners pp
where pp.partner_name LIKE #clientstring AND pp.row_id = (select TOP 1 row_id
from SAP_Master_ProjectPartners pp1
where pp1.project_no = pp.project_no order by update_dt DESC)) c
on (p.project_no = c.project_no) where p.active_flag = 1
ORDER BY p.start DESC
This query would probably be simpler if you defined an entity relationship between SAP_Master_Projects and SAP_Master_ProjectPartners so the join could be implicit instead of explicit.
Edit Since you can't do that, something like this might work (using let and doing a logical join within a where clause):
var clientProjects =
(
from p in entities.SAP_Master_Projects
let c = entities.SAP_Master_ProjectPartners
.Where(cl => cl.partner_name.Contains(clientstring)
&& cl.project_no == p.project_no
)
.OrderBy(cl => cl.update_dt) // Todo: Might need to be descending?
.FirstOrDefault()
where c != null
orderby p.start descending
select new ClientProjects
{
client = c.partner_name,
location = c.city + ", " + c.region,
project_no = c.project_no,
start_dt = p.start,
end_dt = p.finish
}
)
.Take(50)
.ToList()
;
It sounds like you're trying to come up with the following query:
SELECT *
FROM MasterProjects p
INNER JOIN (SELECT project_no,
partner_name
FROM ProjectPartners o
WHERE o.update_dt = (SELECT MAX(update_dt)
FROM ProjectPartners i
WHERE i.project_no = o.project_no)) c
ON p.project_no = c.project_no
AND p.partner_name = c.partner_name
I'm not entirely sure how to translate this in to LINQ but here is my best attempt:
var clientprojects =
from p in MasterProjects
join c in ProjectPartners on p.project_no == c.project_no
where c.partner_name == (from o in ProjectPartners
where o.project_no == c.project_no
and o.update_dt == (from i in ProjectParters
where o.project_no = i.project_no
select i.update_dt).Max()
select o.partner_name).First();
The above LINQ may not even compile, but hopefully it'll send you in the right direction.
I don't speak your language, sorry. But, for instance, in MySql you might add sort by update_dt DESC LIMIT 1 can you do that or somethign similar?

Categories

Resources