Join SQL Server Showing Duplicate Row - sql

I want to ask something about joining query. I have a query like this:
SELECT b.compilecodingid,
a.subjobfamily,
b.position,
b.nocoding,
( CASE
WHEN (SELECT Count(0)
FROM trlspbia
WHERE learningsystemid = a.learningsystemid
AND compilecodingid = b.compilecodingid
AND moduleid = '2018081616230361362303614'
AND learningroadmap = 'Basic') > 0 THEN 1
ELSE 0
END ) AS CountPickPBIA
FROM trlsplanning a,
trcompilecodingheader b
WHERE a.learningsystemid = b.learningsystemid
AND a.position = b.position
AND a.learningsystemid = '2018081513283162000000001'
order by CountPickPBIA desc
I know it's because Column Position on Table TrLsPlanning has more than 1 data,
Anyone can help me to find the solution?
Thank you.

The simplest solution is probably select distinct:
SELECT cch.compilecodingid, p.subjobfamily, cch.position, cch.nocoding,
(CASE WHEN EXISTS (SELECT 1
FROM trlspbia s
WHERE s.learningsystemid = p.learningsystemid AND
s.compilecodingid = ccb.compilecodingid AND
s.moduleid = '2018081616230361362303614' AND
s.learningroadmap = 'Basic'
)
THEN 1
ELSE 0
END) AS CountPickPBIA
FROM trlsplanning p JOIN
trcompilecodingheader cch
ON p.learningsystemid = cch.learningsystemid AND
p.position = cch.position
WHERE p.learningsystemid = '2018081513283162000000001'
ORDER BY CountPickPBIA DESC;
SELECT DISTINCT incurs its own overhead. But without more information about the structure and contents of the table, this is the simplest solution.
Note other changes in the query:
Table aliases are abbreviations for table names, rather than being arbitrary letters.
The JOIN syntax is fixed, to use modern, proper, and standard JOIN/ON.
All columns are qualified with the table alias, particularly those in the correlated subqueries.
The subquery uses EXISTS rather than COUNT(*). This is both more efficient and it probably better expresses the logic you want.

Related

How to use main query column value in sub-query?

I have an SQL query where I want to use one of the column value of the main query in the sub query.
The query is:
select **tool.item**, asset.id, tool.date,
(select freq from workorder
where type = 'CP' and itemnum = **tool.item**) freq, asset.pm
from tool,
asset
where too.num = asset.num
and asset.status = 'ACTIVE';
In this query I want use the fetched tool.item value in the sub query.
item assetid date pm freq
A1 1 12-NOV-15 123 freq from workorder where itemnum ='A1'
A2 2 13-NOV-15 124 freq from workorder where itemnum ='A2'
Could you help me with this?
Thanks in advance.
I strongly encourage you to do two things:
Learn proper JOIN syntax (never use commas in the from clause.
Use abbreviations for table aliases.
So, write the query as:
select t.item, a.id, t.date,
(select wo.freq
from workorder wo
where wo.type = 'CP' and wo.itemnum = t.item
) as freq,
a.pm
from tool t join
asset a
on t.num = a.num
where a.status = 'ACTIVE';
A correlated subquery is a query where the subquery uses columns from the outer query. In this case, the correlation uses t.item in the where clause. When using correlated subqueries I very, very, very strongly recommend that you always use table aliases. It is very easy to make mistakes with column names, and these problems can be quite hard to find.
it's similar to normal join, you need join you subquery in column with your tables in from section
if the query returns null or 1 value it works ok
if it returns more than 1 value you will have exception
select tool.item, asset.id, tool.date,
(select freq from workorder
where type = 'CP' and itemnum = tool.item) freq, asset.pm
from tool,
asset
where tool.num = asset.num
and asset.status = 'ACTIVE';

Providing Language FallBack In A SQL Select Statement

I have a table that represents an Object. It has many columns but also fields that require language support.
For simplicity let's say I have 3 tables:
MainObjectTable
LanguageDependantField1
LanguageDependantField2.
MainObjectTable has a PK int called ID, and both LanguageDependantTables have a foreign key link back to the MainObjectTable along with a language code and the date they were added.
I've created a stored procedure that accepts the MainObjectTable ID and a Language. It will return a single row containing the most recent items from the language tables. The select statement looks like
SELECT
MainObjectTable.VariousColumns,
LanguageDependantField1.Description,
LanguageDependantField2.SomeOtherText
FROM
MainObjectTable
OUTER APPLY
(SELECT TOP 1 LanguageDependantField1.Description
FROM LanguageDependantField1
WHERE LanguageDependantField1.MainObjectTable_ID = MainObjectTable.ID
AND LanguageDependantField1.Language_ID = #language
ORDER BY
LanguageDependantField1.[Default], LanguageDependantField1.CreatedDate DESC) LanguageDependantField1
OUTER APPLY
(SELECT TOP 1 LanguageDependantField2.SomeOtherText
FROM LanguageDependantField2
WHERE LanguageDependantField2.MainObjectTable_ID = MainObjectTable.ID
AND LanguageDependantField2.Language_ID = #language
ORDER BY
LanguageDependantField2.[Default] DESC, LanguageDependantField2.CreatedDate DESC) LanguageDependantField2
WHERE
MainObjectTable.ID = #MainObjectTableID
What I want to add is the ability to fallback to a default language if a row isn't found in the specified language. Let's say we use "German" as the selected language. Is it possible to return an English row from LanguageDependantField1 if the German does not exist presuming we have #fallbackLanguageID
Also am I right to use OUTER APPLY in this scenario or should I be using JOIN?
Many thanks for your help.
Try this:
SELECT MainObjectTable.VariousColumns,
COALESCE(PrefLang.Description,Fallback.Description,'Not Found Desc')
as Description,
COALESCE(PrefLang.SomeOtherText,FallBack.SomeOtherText,'Not found')
as SomeOtherText
FROM MainObjectTable
LEFT JOIN
(SELECT TOP 1 pl.Description,pl.SomeOtherText
FROM LanguageDependantField1 pl
WHERE pl.MainObjectTable_ID = MainObjectTable.ID
AND pl.Language_ID = #language
ORDER BY
pl.[Default], pl.CreatedDate DESC)
PrefLang ON 1=1
LEFT JOIN
(SELECT TOP 1 fb.Description,fb.SomeOtherText
FROM LanguageDependantField1 fb
WHERE fb.MainObjectTable_ID = MainObjectTable.ID
AND fb.Language_ID = #fallbackLanguageID
ORDER BY
fb.[Default], fb.CreatedDate DESC)
Fallback ON 1=1
WHERE
MainObjectTable.ID = #MainObjectTableID
Basically, make two queries, one to the preferred language and one to English (Default). Use the LEFT JOIN, so if the first one isn't found, the second query is used...
I don't have your actual tables, so there might be a syntax error in above, but hope it gives you the concept you want to try...
Yes, the use of Outer Apply is correct if you want to correlate the MainObjectTable table rows to the inner queries. You cannot use Joins with references in the derived table to the outer table. If you wanted to use Joins, you would need to include the joining column(s) and in this case pre-filter the results. Here is what that might look like:
With RankedLanguages As
(
Select LDF1.MainObjectTable_ID, LDF1.Language_ID, LDF1.Description, LDF1.SomeOtherText, ...
, Row_Number() Over ( Partition By LDF1.MainObjectTable_ID, LDF1.Language_ID
Order By LDF1.[Default] Desc, LDF1.CreatedDate Desc ) As Rnk
From LanguageDependantField1 As LDF1
Where LDF1.Language_ID In( #languageId, #defaultLanguageId )
)
Select M.VariousColumns
, Coalesce( SpecificLDF.Description, DefaultLDF.Description ) As Description
, Coalesce( SpecificLDF.SomeOtherText, DefaultLDF.SomeOtherText ) As SomeOtherText
, ...
From MainObjectTable As M
Left Join RankedLanguages As SpecificLDF
On SpecificLDF.MainObjectTable_ID = M.ID
And SpecifcLDF.Language_ID = #languageId
And SpecifcLDF.Rnk = 1
Left Join RankedLanguages As DefaultLDF
On DefaultLDF.MainObjectTable_ID = M.ID
And DefaultLDF.Language_ID = #defaultLanguageId
And DefaultLDF.Rnk = 1
Where M.ID = #MainObjectTableID

SQL Select Where or Having

Im attempting to get some records from a table based on certain factors.
One of the factors is simply with fields on the same table, the other is when joining to another table, I want to compare the number of records in the joined table to a field on the first table. Below is a sample code.
select * from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = 1486
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
-- Having COUNT(LC.ID) = D.Lines
Now from the code above I cant have the Count function in the where clause, and I cant have a field in in the having clause without it being in a function.
Im probably missing something very simple here. But I cant figure it out.
Any help is appreciated it.
EDIT: I do apologise should have explained the structure of the tables, the Destinations are single records, which the LiveCalls table can hold multiple records based on the Destinations ID (foreign key).
Thank you very much for everyones help. My final code:
select D.ID, D.Description, D.Lines, D.Active, D.AlternateFail, D.ConfigurationID, COUNT(LC.ID) AS LiveCalls from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = #ConfigurationID
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
GROUP BY D.ID, D.Description, D.Lines, D.Active, D.AlternateFail, D.ConfigurationID
HAVING COUNT(LC.ID) <= D.Lines
The simple thing you're missing is the GROUP BY statement.
As JNK mentioned in the comments below, you cannot use an aggregate function (such as COUNT, AVG, SUM, MIN) if you don't have a GROUP BY clause, unless your SELECT statement only references literal values (and no column names).
Your code should probably be something like:
SELECT <someFields>
FROM tDestinations D
LEFT JOIN tLiveCalls LC on LC.DestinationID = D.ID
WHERE D.ConfigurationID = 1486
AND (D.Active = 1 AND D.AlternateFail > GETDATE())
GROUP BY <someFields>
HAVING COUNT(LC.ID) = D.Lines
Note that you have to specify the selected fields explicitely, in both the SELECT and GROUP BY statements (no * allowed).
you can only use having with aggregations. Actually having is the "where clause" for aggregation, BUT you can still have a where on the columns that you are no aggregating.
For example:
SELECT TABLE_TYPE, COUNT(*)
FROM INFORMATION_SCHEMA.TABLES
where TABLE_TYPE='VIEW'
group by TABLE_TYPE
having COUNT(*)>1
In your case you need to use havving count(*)=1
so, I think your query would be something like this:
select YOUR_COLUMN
from tDestinations D
left join tLiveCalls LC on LC.DestinationID = D.ID
where D.ConfigurationID = 1486 AND (D.Active = 1 AND D.AlternateFail > GETDATE())
group by YOUR_COLUMN
Having COUNT(LC.ID) = value

SQL Having Clause

I'm trying to get a stored procedure to work using the following syntax:
select count(sl.Item_Number)
as NumOccurrences
from spv3SalesDocument as sd
left outer join spv3saleslineitem as sl on sd.Sales_Doc_Type = sl.Sales_Doc_Type and
sd.Sales_Doc_Num = sl.Sales_Doc_Num
where
sd.Sales_Doc_Type='ORDER' and
sd.Sales_Doc_Num='OREQP0000170' and
sl.Item_Number = 'MCN-USF'
group by
sl.Item_Number
having count (distinct sl.Item_Number) = 0
In this particular case when the criteria is not met the query returns no records and the 'count' is just blank. I need a 0 returned so that I can apply a condition instead of just nothing.
I'm guessing it is a fairly simple fix but beyond my simple brain capacity.
Any help is greatly appreciated.
Wally
First, having a specific where clause on sl defeats the purpose of the left outer join -- it bascially turns it into an inner join.
It sounds like you are trying to return 0 if there are no matches. I'm a T-SQL programmer, so I don't know if this will be meaningful in other flavors... and I don't know enough about the context for this query, but it sounds like you are trying to use this query for branching in an IF statement... perhaps this will help you on your way, even if it is not quite what you're looking for...
IF NOT EXISTS (SELECT 1 FROM spv3SalesDocument as sd
INNER JOINs pv3saleslineitem as sl on sd.Sales_Doc_Type = sl.Sales_Doc_Type
and sd.Sales_Doc_Num = sl.Sales_Doc_Num
WHERE sd.Sales_Doc_Type='ORDER'
and sd.Sales_Doc_Num='OREQP0000170'
and sl.Item_Number = 'MCN-USF')
BEGIN
-- Do something...
END
I didn't test these but off the top of my head give them a try:
select ISNULL(count(sl.Item_Number), 0) as NumOccurrences
If that one doesn't work, try this one:
select
CASE count(sl.Item_Number)
WHEN NULL THEN 0
WHEN '' THEN 0
ELSE count(sl.Item_Number)
END as NumOccurrences
This combination of group by and having looks pretty suspicious:
group by sl.Item_Number
having count (distinct sl.Item_Number) = 0
I'd expect this having condition to approve only groups were Item_Number is null.
To always return a row, use a union. For example:
select name, count(*) as CustomerCount
from customers
group by
name
having count(*) > 1
union all
select 'No one found!', 0
where not exists
(
select *
from customers
group by
name
having count(*) > 1
)

SQL Server: Logical equivalent of ALL query

I have a following query (simplified):
SELECT
Id
FROM
dbo.Entity
WHERE
1 = ALL (
SELECT
CASE
WHEN {Condition} THEN 1
ELSE 0
END
FROM
dbo.Related
INNER JOIN dbo.Entity AS TargetEntity ON
TargetEntity.Id = Related.TargetId
WHERE
Related.SourceId = Entity.Id
)
where {Condition} is a complex dynamic condition on TargetEntity.
In simple terms, this query should return entities for which all related entities match the required condition.
Unfortunately, that does not work quite well, since by SQL standard 1 = ALL evaluates to TRUE when ALL is applied to an empty set. I know I can add AND EXISTS, but that will require me to repeat the whole subquery, which, I am certain, will cause problems for performance.
How should I rewrite the query to achieve the result I need (SQL Server 2008)?
Thanks in advance.
Note: practically speaking, the whole query is highly dynamic, so the perfect solution would be to rewrite only 1 = ALL ( ... ), since changing top-level select can cause problems when additional conditions are added to top-level where.
Couldn't you use a min to achieve this?
EG:
SELECT
Id
FROM
dbo.Entity
WHERE
1 = (
SELECT
MIN(CASE
WHEN {Condition} THEN 1
ELSE 0
END)
FROM
dbo.Related
INNER JOIN dbo.Entity AS TargetEntity ON
TargetEntity.Id = Related.TargetId
WHERE
Related.SourceId = Entity.Id
)
The min should return null if there's no clauses, 1 if they're all 1 and 0 if there's any 0's, and comparing to 1 should only be true for 1.
It can be translated to pick Entities where no related entities with unmatched condition exist.
This can be accomplished by:
SELECT
Id
FROM
dbo.Entity
WHERE
NOT EXISTS (
//as far as I have an element which do not match the condition, skip this entity
SELECT TOP 1 1
FROM
dbo.Related
INNER JOIN dbo.Entity AS TargetEntity ON
TargetEntity.Id = Related.TargetId
WHERE
Related.SourceId = Entity.Id AND
CASE
WHEN {Condition} THEN 1
ELSE 0
END = 0
)
EDIT: depending on condition, you can write something like:
WHERE Related.SourceId = Entity.Id AND NOT {Condition} if it doesn't change too much the complexity of the query.
Instead of using all, change your query to compare the result of the subquery directly:
select Id
from dbo.Entity
where 1 = (
select
case
when ... then 1
else 0
end
from ...
where ...
)
Probably this will work: WHERE NOT 0 = ANY(...)
If I read the query correctly, it can be simplified to something like:
SELECT e.Id
FROM dbo.Entity e
INNER JOIN dbo.Related r ON r.SourceId = e.Id
INNER JOIN dbo.Entity te ON te.Id = r.TargetId
WHERE <extra where stuff>
GROUP BY e.Id
HAVING SUM(CASE WHEN {Condition} THEN 1 ELSE 0 END) = COUNT(*)
This says the Condition must be true for all rows. It filters the "empty" set case away with the INNER JOINs.