Find value that is missing from table - sql

I have 3 tables I am using to compare data and find where a record may be missing from one of the tables. The tables are A_Client, A_USER_Defined and A_Group_Member.
A_CLIENT contains: ClientName, ClientUID
A_USER_Defined contains: ClientUID, LOB
A_GROUP_MEMBER contains: ID, ClientUID
So the common column in each is ClientUID.
I am trying to find the Device(s) where defined.lob = 'value1' and does not exist in the member table where member.id = 'value2'
So far my searches comes up empty and am guessing it's something to do with the different joins? Here is the query I am using:
SELECT A_CLIENT.ClientName
, A_CLIENT.ClientUID
, A_USER_DEFINED.LOB
FROM A_CLIENT
INNER JOIN
A_USER_DEFINED ON A_CLIENT.ClientUID = A_USER_DEFINED.ClientUID
WHERE A_USER_DEFINED.LOB = 'value1'
AND NOT EXISTS (
SELECT ClientUID
FROM A_GROUP_MEMBER
WHERE GroupID = 'value2'
)
Any help would be greatly appreciated.
Thank you.

select cli.ClientName, cli.ClientUID, def.LOB
from A_CLIENT cli
inner join A_USER_DEFINED def on def.ClientGUID = cli.ClientGUID and def.LOB = 'value1'
left join A_GROUP_MEMBER grp on grp.ClientUID = cli.ClientUID and mbr.ID = 'value2'
where mbr.UID is null
select cli.ClientName, cli.ClientUID, def.LOB
from A_CLIENT cli, A_USER_DEFINED def
where def.ClientGUID = cli.ClientGUID and def.LOB = 'value1'
and cli.ClientUID not in (select ClientUID from A_GROUP_MEMBER where GroupID = 'value2')
and various other possibilities, depending on what type of join or predicate your engine is more comfortable with
Edit: the question and the table/field names changed considerably since I posted my answer. They seem to have settled down now and so I changed my code to suit.
Note: the EXISTS predicate in your query only checks whether are any records at all with ID = 'value2'; it should quite probably check only for records that also have a matching UID.
Also, there seems to be some confusion potential between ClientUID and ClientGUID. Is that a typo or does your DB actually have both names in it? If it is the latter then someone is in dire need of being educated with a blunt instrument.

select devices.*
from devices
left join member
on devices.ClientUID = member.ClientUID
and devices.lob = 'value1'
and member.id = 'value2'
where member.ClientUID is null

I think you are actually quite close and just need to correlate the not the exists subquery. Without sample data I haven't tested, but I think that minimal correction will fix your query.
SELECT A_CLIENT.ClientName
, A_CLIENT.ClientUID
, A_USER_DEFINED.LOB
FROM A_CLIENT
INNER JOIN
A_USER_DEFINED ON A_CLIENT.ClientUID = A_USER_DEFINED.ClientUID
WHERE A_USER_DEFINED.LOB = 'value1'
AND NOT EXISTS (
SELECT ClientUID
FROM A_GROUP_MEMBER
WHERE A_GROUP_MEMBER.ClientUID = A_CLIENT.ClientUID
AND GroupID = 'value2'
)
I'd probably use some table alias ti make that easier to read but ymmv:
SELECT c.ClientName
, c.ClientUID
, u.LOB
FROM A_CLIENT c
INNER JOIN
A_USER_DEFINED u ON c.ClientUID = u.ClientUID
WHERE A_USER_DEFINED.LOB = 'value1'
AND NOT EXISTS (
SELECT 1
FROM A_GROUP_MEMBER g
WHERE g.ClientUID = c.ClientUID
AND GroupID = 'value2'
)

Related

Multiple Joins less cost way

Below query has 3 tables where I have to do 2 joins to get a column information, It is very slow, is there any effective way to run this query?
SELECT DISTINCT
st.status_c1
FROM
schemaname.tablea st
INNER JOIN (
SELECT
lic.SpecId AS applicationid,
lic.comData AS combusappid,
lic.ageId,
lic.licId,
lic.licid,
lic.appid,
com.nybe_bustbl_id AS busid
FROM
schemaname.tableb lic
INNER JOIN tablec com ON lic.comData = com.comData
WHERE
lic.ageId = '12'
) rt ON
st.ageId = rt.ageId
AND
st.licId = rt.licId
AND
st.licid = rt.licid
AND
st.appid = rt.appid
WHERE
status_id = 3;
Your current query will create extra rows when the JOIN condition is met for multiple entries in either table and then DISTINCT will filter these duplicates out. You could try to cut down the amount of work filtering duplicates by using EXISTS:
SELECT DISTINCT
st.status_c1
FROM schemaname.tablea st
WHERE status_id = 3
AND EXISTS (
SELECT 1
FROM schemaname.tableb lic
WHERE lic.ageId = '12'
AND st.ageId = lic.ageId
AND st.licId = lic.licId
AND st.appid = lic.appid
AND EXISTS(
SELECT 1 FROM tablec com WHERE lic.comData = com.comData
)
);
There is a bunch of redundancy in the query (licid is in the SELECT and ON twice) and you don't need to use subqueries for this. I think this will work:
SELECT DISTINCT st.status_c1
FROM tablea st
INNER JOIN tableb lic ON st.ageId = lic.ageId
AND st.licId = lic.licId
AND st.appid = lic.appid
INNER JOIN tablec com ON lic.comData = com.comData
WHERE status_id = 3
and lic.ageId = '12'
How frequently are you going to run this query, how much time is it taking now and what is the explectation. Are statistcs run on all tha tables.
There are many things which we can think of, but to start with if possible could you plese give ue the like the table structure and explain plan of the query.
Also may be an index on status_c1 table tablea help. As pointed out try removing the join condition which is twice AND st.licid = rt.licid
SELECT DISTINCT st.status_c1
FROM schemaname.tablea st
INNER JOIN (
SELECT
lic.SpecId AS applicationid, lic.comData AS combusappid, lic.ageId, lic.licId, lic.licid,
lic.appid, com.nybe_bustbl_id AS busid
FROM schemaname.tableb lic
INNER JOIN tablec com ON lic.comData = com.comData
WHERE lic.ageId = '12'
) rt ON st.ageId = rt.ageId AND st.licId = rt.licId AND st.licid = rt.licid AND st.appid = rt.appid
WHERE status_id = 3;

Change existing sql to left join only on first match

Adding back some original info for historical purposes as I thought simplifying would help but it didn't. We have this stored procedure, in this part it is selecting records from table A (calldetail_reporting_agents) and doing a left join on table B (Intx_Participant). Apparently there are duplicate rows in table B being pulled that we DON'T want. Is there any easy way to change this up to only pick the first match on table B? Or will I need to rewrite the whole thing?
SELECT 'Agent Calls' AS CallType,
CallDate,
CallTime,
RemoteNumber,
DialedNumber,
RemoteName,
LocalUserId,
CallDurationSeconds,
Answered,
AnswerSpeed,
InvalidCall,
Intx_Participant.Duration
FROM calldetail_reporting_agents
LEFT JOIN Intx_Participant ON calldetail_reporting_agents.CallID = Intx_Participant.CallIDKey
WHERE DialedNumber IN ( SELECT DialedNumber
FROM #DialedNumbers )
AND ConnectedDate BETWEEN #LocStartDate AND #LocEndDate
AND (#LocQueue IS NULL OR AssignedWorkGroup = #LocQueue)
Simpler version: how to change below to select only first matching row from table B:
SELECT columnA, columnB FROM TableA LEFT JOIN TableB ON someColumn
I changed to this per the first answer and all data seems to look exactly as expected now. Thank you to everyone for the quick and attentive help.
SELECT 'Agent Calls' AS CallType,
CallDate,
CallTime,
RemoteNumber,
DialedNumber,
RemoteName,
LocalUserId,
CallDurationSeconds,
Answered,
AnswerSpeed,
InvalidCall,
Intx_Participant.Duration
FROM calldetail_reporting_agents
OUTER APPLY (SELECT TOP 1
*
FROM Intx_Participant ip
WHERE calldetail_reporting_agents.CallID = ip.CallIDKey
AND calldetail_reporting_agents.RemoteNumber = ip.ConnValue
AND ip.HowEnded = '9'
AND ip.Recorded = '0'
AND ip.Duration > 0
AND ip.Role = '1') Intx_Participant
WHERE DialedNumber IN ( SELECT DialedNumber
FROM #DialedNumbers )
AND ConnectedDate BETWEEN #LocStartDate AND #LocEndDate
AND (#LocQueue IS NULL OR AssignedWorkGroup = #LocQueue)
You can try to OUTER APPLY a subquery getting only one matching row.
...
FROM calldetail_reporting_agents
OUTER APPLY (SELECT TOP 1
*
FROM intx_Participant ip
WHERE ip.callidkey = calldetail_reporting_agents.callid) intx_participant
WHERE ...
You should add an ORDER BY in the subquery. Otherwise it isn't deterministic which row is taken as the first. Or maybe that's not an issue.

Trying to understand a query (LEFT JOIN and subquery)

I've been trying to understand the behavior of a query but i dont fully understand what is going on.
Take a look:
SELECT main.entity_id,main.sku,name.value AS name
FROM product_entity AS main
LEFT JOIN product_entity_varchar AS name ON main.entity_id = name.entity_id
WHERE name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
Can you please explain why it is using a subquery ,why the LEFT JOIN is important in these cases and the condition entity_type_code = 'catalog_product_info'?
Thanks
SELECT main.entity_id,main.sku,name.value AS name
FROM product_entity AS main
LEFT JOIN product_entity_varchar AS name ON main.entity_id = name.entity_id
...
The query starts by pulling out table product_entity. The LEFT JOIN allows the query to access the record(s) in table product_entity_varchar whose entity_id is equal to the value of the column that has the same name in product_entity.
In the resultset, the value of column value from table product_entity_varchar is displayed, under alias name.
The keyword LEFT makes the relation optional ; it there is no matching record in product_entity_varchar, the name will simply appear as NULL in the output. If it were an [INNER] JOIN, then the relation would be mandatory : unmatched records would be filtered out, and would not appear in the output.
OK. Let's clarify what is LEFT OUTER JOIN in SQL.
LEFT JOIN queries could be understood as just syntax abbreviation of UNION ALL of INNER JOIN-ed and NOT EXIST-ed queries.
Let's decompose LEFT JOIN-ed query:
SELECT main.entity_id,main.sku,name.value AS name
FROM
product_entity AS main
LEFT JOIN product_entity_varchar AS name ON main.entity_id = name.entity_id
WHERE
name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
Is equivalent to:
SELECT main.entity_id,main.sku,name.value AS name
FROM
product_entity AS main
JOIN product_entity_varchar AS name
ON main.entity_id = name.entity_id
WHERE
name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
UNION ALL
SELECT main.entity_id,main.sku, NULL AS name -- <-- Attention!
FROM
product_entity AS main
NOT EXISTS(
SELECT * FROM product_entity_varchar AS name
WHERE
main.entity_id = name.entity_id
AND name.attribute_id = (
SELECT attribute_id
FROM ger_attribute
WHERE attribute_code LIKE "name"
AND 'entity_type_id' = (
SELECT entity_type_id
FROM ger_entity_type
WHERE entity_type_code = 'catalog_product_info'
)
)
)
I hope this will help to understand query, although it's impossible without data to answer "why it is using a subquery" etc.

Check if a combination of fields already exists in the table

My weakest area of SQL are self JOINS, currently struggling with an issue.
I need to find the latest entry in a table, I'm using a WHERE DATEFIELD IN (SELECT MAX(DATEFIELD) FROM TABLE) to do this. I then need to establish if 3 columns from that already exist in the same TABLE.
My latest attempt looks like this -
SELECT * FROM PART_TABLE
WHERE NOT EXISTS
(
SELECT
t1.DATEFIELD
t1.CODE1
t1.CODE2
t1.CODE3
FROM PART_TABLE t1
INNER JOIN PART_TABLE t2 ON t1.UNIQUE = t2.UNQIUE
)
WHERE t1.DATEFIELD IN
(
SELECT MAX(DATEFIELD)
FROM PARTTABLE
)
)
I think part of the issue is that I can't exclude the unique row from t1 when checking in t2 using this method.
Using MSSQL 2014.
The following query will return the latest record from your table and a bit flag whether a duplicate tuple {Code1, Code2, Code3} exists in it under a different identifier:
select top (1) p.*,
case when exists (
select 0 from dbo.Part_Table t where t.Unique != p.Unique
and t.Code1 = p.Code1 and t.Code2 = p.Code2 and t.Code3 = p.Code3
) then 1
else 0 end as [IsDuplicateExists]
from dbo.Part_Table p
order by p.DateField desc;
You can use this example as a template to address your specific needs, which unfortunately aren't immediately apparent from your explanation.

SQL Elaborate Joins Query

I'm trying to solve the below problem.
I feel like it is possible, but I can't seem to get it.
Here's the scenario:
Table 1 (Assets)
1 Asset-A
2 Asset-B
3 Asset-C
4 Asset-D
Table 2 (Attributes)
1 Asset-A Red
2 Asset-A Hard
3 Asset-B Red
4 Asset-B Hard
5 Asset-B Heavy
6 Asset-C Blue
7 Asset-C Hard
If I am looking for something having the same attributes as Asset-A, then it should identify Asset-B since Asset-B has all the same attributes as Asset-A (it should discard heavy, since Asset-A didn't specify anything different or the similar). Also, if I wanted the attributes for only Asset-A AND Asset-B that were common, how would I get that?
Seems simple, but I can't nail it...
The actual table I am using, is almost precisely Table2, simply an association of an AssetId, and an AttributeId so:
PK: Id
int: AssetId
int: AttributeId
I only included the idea of the asset table to simplify the question.
SELECT ato.id, ato.value
FROM (
SELECT id
FROM assets a
WHERE NOT EXISTS
(
SELECT NULL
FROM attributes ata
LEFT JOIN
attributes ato
ON ato.id = ata.id
AND ato.value = ata.value
WHERE ata.id = 1
AND ato.id IS NULL
)
) ao
JOIN attributes ato
ON ato.id = ao.id
JOIN attributes ata
ON ata.id = 1
AND ata.value = ato.value
, or in SQL Server 2005 (with sample data to check):
WITH assets AS
(
SELECT 1 AS id, 'A' AS name
UNION ALL
SELECT 2 AS id, 'B' AS name
UNION ALL
SELECT 3 AS id, 'C' AS name
UNION ALL
SELECT 4 AS id, 'D' AS name
),
attributes AS
(
SELECT 1 AS id, 'Red' AS value
UNION ALL
SELECT 1 AS id, 'Hard' AS value
UNION ALL
SELECT 2 AS id, 'Red' AS value
UNION ALL
SELECT 2 AS id, 'Hard' AS value
UNION ALL
SELECT 2 AS id, 'Heavy' AS value
UNION ALL
SELECT 3 AS id, 'Blue' AS value
UNION ALL
SELECT 3 AS id, 'Hard' AS value
)
SELECT ato.id, ato.value
FROM (
SELECT id
FROM assets a
WHERE a.id <> 1
AND NOT EXISTS
(
SELECT ata.value
FROM attributes ata
WHERE ata.id = 1
EXCEPT
SELECT ato.value
FROM attributes ato
WHERE ato.id = a.id
)
) ao
JOIN attributes ato
ON ato.id = ao.id
JOIN attributes ata
ON ata.id = 1
AND ata.value = ato.value
I don't completely understand the first part of your question, identifying assets based on their attributes.
Making some assumptions about column names, the following query would yield the common attributes between Asset-A and Asset-B:
SELECT [Table 2].Name
FROM [Table 2]
JOIN [Table 1] a ON a.ID = [Table 2].AssetID AND a.Name = 'Asset-A'
JOIN [Table 1] b ON b.ID = [Table 2].AssetID AND b.Name = 'Asset-B'
GROUP BY [Table 2].Name
Select * From Assets A
Where Exists
(Select * From Assets
Where AssetId <> A.AssetID
And (Select Count(*)
From Attributes At1 Join Attributes At2
On At1.AssetId <> At2.AssetId
And At1.attribute <> At2.Attribute
Where At1.AssetId = A.AssetId Asset) = 0 )
And AssetId = 'Asset-A'
select at2.asset, count(*)
from attribute at1
inner join attribute at2 on at1.value = at2.value
where at1.asset = "Asset-A"
and at2.asset != "Asset-A"
group by at2.asset
having count(*) = (select count(*) from attribute where asset = "Asset-A");
Find all assets who have every attribute that "A" has (but also may have additional attributes):
SELECT Other.ID
FROM Assets Other
WHERE
Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself
AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE
AttA.AssetID='Asset-A'
AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE
AttOther.AssetID=Other.ID AND AttOther.AttributeID = AttA.AttributeID
)
)
I.e., "find any asset where there is no attribute of A that is not also an attribute of this asset".
Find all assets who have exactly the same attributes as "A":
SELECT Other.ID
FROM Assets Other
WHERE
Other.AssetID <> 'Asset-A' -- do not return Asset A as a match to itself
AND NOT EXISTS (SELECT NULL FROM Attributes AttA WHERE
AttA.AssetID='Asset-A'
AND NOT EXISTS (SELECT NULL FROM Attributes AttOther WHERE
AttOther.AssetID=Other.ID
AND AttOther.AttributeID = AttA.AttributeID
)
)
AND NOT EXISTS (SELECT NULL FROM Attributes AttaOther WHERE
AttaOther.AssetID=Other.ID
AND NOT EXISTS (SELECT NULL FROM Attributes AttaA WHERE
AttaA.AssetID='Asset-A'
AND AttaA.AttributeID = AttaOther.AttributeID
)
)
I.e., "find any asset where there is no attribute of A that is not also an attribute of this asset, and where there is no attribute of this asset that is not also an attribute of A."
This solution works as prescribed, thanks for the input.
WITH Atts AS
(
SELECT
DISTINCT
at1.[Attribute]
FROM
Attribute at1
WHERE
at1.[Asset] = 'Asset-A'
)
SELECT
DISTINCT
Asset,
(
SELECT
COUNT(ta2.[Attribute])
FROM
Attribute ta2
INNER JOIN
Atts b
ON
b.[Attribute] = ta2.[attribute]
WHERE
ta2.[Asset] = ta.Asset
)
AS [Count]
FROM
Atts a
INNER JOIN
Attribute ta
ON
a.[Attribute] = ta.[Attribute]
Find all assets that have all the same attributes as asset-a:
select att2.Asset from attribute att1
inner join attribute att2 on att2.Attribute = att1.Attribute and att1.Asset <> att2.Asset
where att1.Asset = 'Asset-A'
group by att2.Asset, att1.Asset
having COUNT(*) = (select COUNT(*) from attribute where Asset=att1.Asset)
I thought maybe I can do this with LINQ and then work my way backwards with:
var result = from productsNotA in DevProducts
where productsNotA.Product != "A" &&
(
from productsA in DevProducts
where productsA.Product == "A"
select productsA.Attribute
).Except
(
from productOther in DevProducts
where productOther.Product == productsNotA.Product
select productOther.Attribute
).Single() == null
select new {productsNotA.Product};
result.Distinct()
I thought that translating this back to SQL with LinqPad would result into a pretty SQL query. However it didn't :). DevProducts is my testtable with a column Product and Attribute. I thought I'd post the LINQ query anyways, might be useful to people who are playing around with LINQ.
If you can optimize the LINQ query above, please let me know (it might result in better SQL ;))
I'm using following DDL
CREATE TABLE Attributes (
Asset VARCHAR(100)
, Name VARCHAR(100)
, UNIQUE(Asset, Name)
)
Second question is easy
SELECT Name
FROM Attributes
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A')
AND Asset = 'B'
First question is not more difficult
SELECT Asset
FROM Attributes
WHERE Name IN (SELECT Name FROM Attributes WHERE Asset = 'A')
GROUP BY Asset
HAVING COUNT(*) = (SELECT COUNT(*) FROM FROM Attributes WHERE Asset = 'A')
Edit:
I left AND Asset != 'A' out of the WHERE clause of the second snippet for brevity