Taking Count of Subqueries in Full Outer Join - sql-server

I am working on SQL Server 2012. My SQL follows the following structure.
SELECT A.attributeA
,A.attributeB
,Count(A.*) AS CountA -- I know this is wrong.
,Count(B.*) AS CountB
FROM
(
SELECT ... FROM Foo1
) A
FULL OUTER JOIN
(
SELECT ... FROM Foo2
) B
ON A.attribute1 = B.attribute1
GROUP BY
A.attributeA
,A.attributeB
I want to take the count of all rows from subqueries A and B. How do I do that? Thank you in advance.

Assuming the goal is to just count the non-null records from each side of the join, you can specify the column name (as mentioned in a comment) that you expect to be non-null, often the same column as in your join. For example, since you joined on attribute1:
SELECT A.attributeA
,A.attributeB
,Count(A.attribute1) AS CountA
,Count(B.attribute1) AS CountB
FROM
...
Note that this tells you nothing about when the 2 overlap, if that is part of your goal. For that type of counting, you can use a SUM combined with CASE:
SELECT A.attributeA
,A.attributeB
,Count(A.attribute1) AS CountA
,Count(B.attribute1) AS CountB
,SUM(CASE WHEN A.attribute1 IS NOT NULL AND B.attribute1 IS NOT NULL
THEN 1 ELSE 0
END) as CountAAndBOverlap
FROM
...

If your purpose is just to get the count of the two sub-queries, you can do something like this one. I've also generated common ID for the two sub-queries so that I can JOIN them.Lastly, I used INNER JOIN instead of FULL JOIN.
SELECT
CountSubqueryA AS CountA,
CountSubqueryB AS CountB
FROM
(SELECT 1 AS ID,COUNT(*) AS CountSubqueryA FROM Foo1 ) AS A
INNER JOIN
(SELECT 1 AS ID,COUNT(*) AS CountSubqueryB FROM Foo2 ) AS B
ON A.ID=B.ID

I saw your GROUP BY and thought maybe you wanted this. It will create up to 3 groups that look something like this:
==========================
| A | NOT B | 24 |
--------------------------
| NOT A | B | 31 |
--------------------------
| A | B | 69 |
==========================
SELECT
CASE WHEN A.attribute1 IS NOT NULL THEN 'A' ELSE 'NOT A' END,
CASE WHEN B.attribute1 IS NOT NULL THEN 'B' ELSE 'NOT B' END
COUNT(*)
FROM
(
SELECT ... FROM Foo1
) A
FULL OUTER JOIN
(
SELECT ... FROM Foo2
) B
ON A.attribute1 = B.attribute1
GROUP BY
CASE WHEN A.attribute1 IS NOT NULL THEN 'A' ELSE 'NOT A' END,
CASE WHEN B.attribute1 IS NOT NULL THEN 'B' ELSE 'NOT B' END

Related

Why is this SQL case statement behaving like an OR statement?

Consider the following query:
declare #RentalId int = 1
SELECT
r.RentalId
,r.[Name]
,rt.TypeId
FROM dbo.Rental r
LEFT JOIN dbo.RentalRateType rt ON (
r.RentalId = rt.RentalId
AND rt.TypeId = (
case when rt.TypeId = 6 and coalesce(rt.[Max], rt.[Min]) is not null then 6
when rt.TypeId = 1 and coalesce(rt.[Max], rt.[Min] is not null then 1
else -1 end
))
WHERE r.RentalId = #RentalId
I'm attempting to return a single record/row. The particular rental in question has 2 records in the dbo.RentalRateType table, and when I run the above query, I get 2 results, but I want it to short circuit on the first match in the case where.
Basically, the end user can fill in multiple rate types, more than what you see in this example, and each of those types has a priority. 6 is the highest priority in the example.
So I'm getting this result:
RentalId | Name | TypeId
----------------------------
1 Super Car 6
1 Super Car 1
But if the type (6) exists, I would expect only the first row above returned.
I must be missing something silly. This works as expected:
case when 1=2 then 6
when 1=1 then 1
else -1 end
While I'm here, I'm open to a more efficient manner of handling this if exists.
Use an apply instead, these are an efficient way to get "top n" queries:
SELECT
r.RentalId
, r.[Name]
, oa.TypeId
FROM dbo.Rental r
OUTER APPLY (
SELECT TOP (1)
rt.TypeId
FROM dbo.RentalRateType rt
WHERE r.RentalId = rt.RentalId
ORDER BY
rt.TypeId DESC
) oa
WHERE r.RentalId = #RentalId

Select and compare two Datetime columns from different table without having any relation

I have two Tables with below format
**Table1**
"date_Time1"
"2018-09-13 04:27:00.000"
"2018-09-13 04:28:00.000"
**Table2**
"date_Time2"
"2018-09-13 04:27:00.000"
"2018-09-13 04:29:00.000"
Now I want to comapre both these tables whether the datetime is matching or not.
The Result should like
+--------------------------+---------+
| Date_Time | Is_match|
+--------------------------+---------+
|"2018-09-13 04:27:00.000" | True |
|"2018-09-13 04:28:00.000" | False |
|"2018-09-13 04:29:00.000" | False |
+-----------------------------------+
Another possible approach, which will find duplicate dates between two tables and possible duplicate dates in each table:
WITH cte (date_time) AS (
SELECT date_time1
FROM DateTimeTable1
UNION ALL
SELECT date_time2
FROM DateTimeTable2
)
SELECT
date_time,
CASE
WHEN COUNT(*) > 1 THEN 'True'
ELSE 'False'
END AS is_match
FROM cte
GROUP BY date_time
ORDER BY date_time
You can try below query
select coalesce(t1.date_time1,t2.date_time2) as Date_Time ,
case when t1.date_time1 = t2.date_time2 then 'True' else 'False' end as Is_Match
from table1 t1 full outer join table2 t2 on t1.date_time1=t2.date_time2
Try this:
SELECT ISNULL(t1.Date_Time,t2.Date_Time) AS Date_Time,
CASE WHEN t1.Date_Time=t2.Date_Time THEN 'True' ELSE 'False' END AS Is_Match
FROM Table1 t1
OUTER JOIN Table2 t2 ON t1.Date_Time=t2.Date_Time

MSSQL/TSQL Joining against a subquery

I'm analyzing IIS log files from sharepoint and need to match each entry to it's SPWeb.
This SQL code works for a single value (#var1):
DECLARE #var1 varchar(128);
set #var1 = '/sites/Site1/Subsite1/Subsite2/Documents/marketing.docx';
select
TOP 1 *,
charindex(urlstub, #var1) as found
from
spwebs
where
charindex(urlstub, #var1) = 1
order by
urlstub DESC;
I'm looking for a way to get this to work for a tables worth of data instead of just the single variable #var1.
Example data
SPwebs:
/sites/Site1
/sites/Site1/Subsite1
/sites/Site1/Subsite1/Subsite2
/sites/Site2
etc..
IISlog: (this is the table I'd like to take the place of #var1 above)
/sites/Site1/Subsite1/Subsite2/Documents/marketing.docx
/sites/Site1/Subsite1/Subsite2/Documents/sales.docx
/sites/Site1/Subsite1/Subsite2/Documents/hr.docx
/sites/Site1/research/funding.docx
The expected outcome of the above would be:
Foreach record in the IISLog table:
Find the best/deepest matching record from the spwebs table:
|table | matchingSPweb |
|---------------------------------------------------------| --------------------------------|
| /sites/Site1/Subsite1/Subsite2/Documents/marketing.docx | /sites/Site1/Subsite1/Subsite2/ |
| /sites/Site1/Subsite1/Subsite2/Documents/sales.docx | /sites/Site1/Subsite1/Subsite2/ |
| /sites/Site1/Subsite1/Subsite2/Documents/hr.docx | /sites/Site1/Subsite1/Subsite2/ |
| /sites/Site1/research/funding.docx | /sites/Site1 |
I've tried
select iislogs2.*, spwebs.urlstub
from
iislogs2
inner join
(
select TOP 1 urlstub, csURIStem as found
from spwebs
where charindex(urlstub, iislogs2.csUriStem) = 1
order by urlstub DESC
) as x
on x.csuristem = iislogs2.csUriStem
but this just errors, it doesn't seem to understand csUriStem in the context of the subselect statement.
The easiest ways to fix your issue are either to change your current query to use a subquery in the select statement, e.g.:
SELECT iislogs2.*,
urlstub = (SELECT TOP 1 urlstub FROM spwebs WHERE CHARINDEX(urlstub, iislogs2.csUriStem) = 1 ORDER BY urlstub DESC)
from iislogs2;
... or change your current join to a cross apply, e.g.:
SELECT iislogs2.*, x.urlstub
from iislogs2
cross apply (SELECT TOP 1 urlstub FROM spwebs WHERE CHARINDEX(urlstub, iislogs2.csUriStem) = 1 ORDER BY urlstub DESC) AS x;
EDIT:
The query optimiser might do all sorts of weird sorts and spools, so one option to avoid that might be to use an explicit join with a CTE and then left join this back to your original table. For example:
;WITH CTE AS
(
SELECT i.csUriStem, s.urlstub, RN = ROW_NUMBER() OVER (PARTITION BY i.csUriStem ORDER BY s.urlstub DESC)
FROM iislogs2 AS i
JOIN spwebs AS s
ON i.csUriStem LIKE s.urlstub + '%'
)
SELECT i.*, c.urlstub
FROM iislogs2 AS i
LEFT JOIN CTE AS c
ON c.csUriStem = i.csUriStem
AND c.RN = 1;
Unfortunately, with strings and substrings, it's hard to get an execution plan that is really optimal for what you want to do, but I expect this sort of query will perform better with indexes than the other two.

SQL Server : how to declare variable in condition case when

I have a problem when running queries with a 'case when'. so I want to create a variable to hold the value of the results of the query 'select' to other tables. like the example below. please help me to get the results I want. thank you.
SELECT
a.field1, a.field2,
a.field3 =
CASE
WHEN a.field1 = 'alfa'
THEN
WHEN
-- I want to declare variable to check get value from another table with query select and condition 'WHERE' with variable declare
var varTest = (SELECT TOP 1 b.field1 FROM Table2 as b WHERE b.field2=a.field2) Then
if varTest = 'actif' then
SELECT (c.field4 * C.field5) as hasil FROM Table2 as c WHERE c.field1=varTest)
ELSE
a.field3
END
FROM
Table1 a(NOLOCK)
WHERE
a.field1 = 'alfa'
This is sample data :
field1 | field2 | field3 | > Table1
alfa idAlfa 0
beta idBeta 0
carlie idCarlie 0
field1 | field2 | field4 | field5 | > Table2
actif idAlfa 80 5
pasif idBeta 50 5
other idCarlie 10 5
Result :
field1 | field2 | field3
alfa idAlfa 400
beta idBeta 250
carlie idCarlie 50
It looks to me like this is what you intended. Hard to be entirely sure. It's also possible that this isn't the most efficient query as it's generally a good idea to avoid subqueries inside case expressions.
a.field3 =
CASE WHEN a.field1 = 'alfa' THEN
CASE WHEN (
SELECT TOP 1 b.field1 FROM Table2 as b
WHERE b.field2 = a.field
) = 'actif'
THEN (
SELECT c.field4 * C.field5 as hasil FROM Table2 as c
WHERE c.field1 = 'actif'
)
ELSE a.field3
END
ELSE null
END
You don't need a variable of any type to accomplish this.
A simple JOIN and CASE will get the active (actif) record, calculate your field4 * field5 result (hasil) or use 0 for all passive (pasif) or other records like so:
SELECT
a.field1,
a.field2,
CASE b.field1 -- check id...
WHEN 'actif'
THEN b.field4 * b.field5 -- if 'actif' record, calculate result
ELSE
a.field3 -- not the 'actif' record, use default value
END AS hasil
FROM Table1 AS a -- join the two tables by id name
LEFT JOIN Table2 AS b
ON a.field2 = b.field2;
If you're using SQL Server 2012, you can condense this CASE statement by using IIF:
SELECT
a.field1,
a.field2,
IIF(b.field1 = 'actif', b.field3 * b.field4, a.field3) AS hasil
FROM Table1 AS a
LEFT JOIN Table2 AS b
ON a.field2 = b.field2;
The results using your sample data from these queries are:
field1 | field2 | hasil
alfa idAlfa 400
beta idBeta 0
charlie idCharlie 0
For future reference, the IF syntax you showed in your sample SQL is invalid. That is used in flow control statements that affect how SQL is executed; usually seen in stored procedures. IF can't be used in expressions. Instead, use CASE or IIF statements.

EF6 - Generating unneeded nested queries

I have the following tables:
MAIN_TBL:
Col1 | Col2 | Col3
------------------
A | B | C
D | E | F
And:
REF_TBL:
Ref1 | Ref2 | Ref3
------------------
A | G1 | Foo
D | G1 | Bar
Q | G2 | Xyz
I wish to write the following SQL query:
SELECT M.Col1
FROM MAIN_TBL M
LEFT JOIN REF_TBL R
ON R.Ref1 = M.Col1
AND R.Ref2 = 'G1'
WHERE M.Col3 = 'C'
I wrote the following LINQ query:
from main in dbContext.MAIN_TBL
join refr in dbContext.REF_TBL
on "G1" equals refr.Ref2
into refrLookup
from refr in refrLookup.DefaultIfEmpty()
where main.Col1 == refr.Col1
select main.Col1
And the generated SQL was:
SELECT
[MAIN_TBL].[Col1]
FROM (SELECT
[MAIN_TBL].[Col1] AS [Col1],
[MAIN_TBL].[Col2] AS [Col2],
[MAIN_TBL].[Col3] AS [Col3]
FROM [MAIN_TBL]) AS [Extent1]
INNER JOIN (SELECT
[REF_TBL].[Ref1] AS [Ref1],
[REF_TBL].[Ref2] AS [Ref2],
[REF_TBL].[Ref3] AS [Ref3]
FROM [REF_TBL]) AS [Extent2] ON [Extent1].[Col1] = [Extent2].[Ref1]
WHERE ('G1' = [Extent2].[DESCRIPTION]) AND ([Extent2].[Ref1] IS NOT NULL) AND CAST( [Extent1].[Col3] AS VARCHAR) = 'C') ...
Looks like it is nesting a query within another query, while I just want it to pull from the table. What am I doing wrong?
I may be wrong, but it looks like you don't do the same in linq query and sql query, especially on your left joining clause.
I would go for this, if you want something similar to your sql query.
from main in dbContext.MAIN_TBL.Where(x => x.Col3 == "C")
join refr in dbContext.REF_TBL
on new{n = "G1", c = main.Col1} equals new{n = refr.Ref2, c = refr.Col1}
into refrLookup
from r2 in refrLookup.DefaultIfEmpty()
select main.Col1
By the way, it doesn't make much sense to left join on a table which is not present in the select clause : you will just get multiple identical Col1 if there's more than one related item in the left joined table...

Resources