Cross join on a group based on the condition in SQL Server - sql

I have Item and Exam tables -
My desire result -
I tried a lot but I am unable to join Exam table to Item table based on Code group.
Is it possible to cross join or any other join for these two tables to get the desire result?

You can try this
SELECT A.Code, i.Item, A.Exam
FROM
(
SELECT Code,e.Exam
FROM Item i
CROSS JOIN Exam e
GROUP BY Code,e.Exam
) A
LEFT JOIN Item i ON i.Code= A.Code AND i.Exam = A.Exam

I don't see a way around generating the missing data. The calendar table approach would be to cross join all codes with all exams. Then, left join this table to Item and order to get the result you want:
WITH cte AS (
SELECT *
FROM (SELECT DISTINCT Code FROM Item) AS C
CROSS JOIN Exam
)
SELECT
t1.Code,
t2.Item,
t1.Exam
FROM cte t1
LEFT JOIN Item t2
ON t1.Code = t2.Code AND
t1.Exam = t2.Exam
ORDER BY
t1.Code,
CASE WHEN t2.Item IS NOT NULL THEN 0 ELSE 1 END,
t1.Exam
Demo here:
Rextester

This should produce what you need. I am using UNION to combine CROSS JOIN and item table and then group by to ensure NULLs are removed.
SELECT code, max(item) as item, exam
FROM
(SELECT distinct i.code, null as item, e.exam FROM exam e cross join item i
union all
SELECT code, item, exam
FROM item) u
group by code, exam
order by code, exam

Related

Postgresql - Group By

I have a simple groupby scenario. Below is the output of the query.
Query is:
select target_date, type, count(*) from table_name group by target_date, type
The query and output is perfectly good.
My problem is I am using this in Grafana for plotting. That is Grafana with postgres as backend.
What happens is since "type2" category is missed on 01-10-2020 and 03-10-2020, type2 category never gets plotted (side to side bar plot) at all. Though "type2" is present in other days.
It is expecting some thing like
So whenever a category is missed in a date, we need a count with 0 value.
Need to handle this in query, as the source data cannot be modified.
Any help here is appreciated.
You need to create a list of all the target_date/type combinations. That can be done with a CROSS JOIN of two DISTINCT selects of target_date and type. This list can beLEFT JOINed to table_name to get counts for each combination:
SELECT dates.target_date, types.type, COUNT(t.target_date)
FROM (
SELECT DISTINCT target_date
FROM table_name
) dates
CROSS JOIN (
SELECT DISTINCT type
FROM table_name
) types
LEFT JOIN table_name t ON t.target_date = dates.target_date AND t.type = types.type
GROUP BY dates.target_date, types.type
ORDER BY dates.target_date, types.type
Demo on dbfiddle
You may use a calendar table approach here:
SELECT
t1.target_date,
t2.type,
COUNT(t3.target_date) AS count
FROM (SELECT DISTINCT target_date FROM yourTable) t1
CROSS JOIN (SELECT DISTINCT type FROM yourTable) t2
LEFT JOIN yourTable t3
ON t3.target_date = t1.target_date AND
t3.type = t2.type
GROUP BY
t1.target_date,
t2.type
ORDER BY
t1.target_date,
t2.type;
The idea here is to cross join subqueries finding all distinct target dates and types, to generate a starting point for the query. Then, we left join this intermediate table to your actual table, and find the counts for each date and type.
select t.target_date, tmp.type, sum(case when t.type = tmp.type then 1 else 0 end)
from your_table t
cross join (select distinct type from your_table) tmp
group by t.target_date, tmp.type
Demo

Count records only from left side of a LEFT JOIN

I'm building an Access query with a LEFT JOIN that, among other things, counts the number of unique sampleIDs present in the left table of the JOIN, and counts the aggregate number of specimens (bugs) present in the right table of the JOIN, both for a given group of samples (TripID). Here's the pertinent chunk of SQL code:
SELECT DISTINCT t1.TripID, COUNT(t1.SampleID) AS Samples, SUM(t2.C1 + t2.C2)
AS Bugs FROM tbl_Sample AS t1
LEFT JOIN tbl_Bugs AS t2 ON t1.SampleID = t2.SampleID
GROUP BY t1.TripID
The trouble I'm having is that COUNT(t1.SampleID) is not giving me my desired result. My desired result is the number of unique SampleIDs present in t1 for a given TripID (let's say 7). Instead, what I get seems to be the number of rows in t2 for which the SampleID is contained within the given TripID group (let's say 77). How can I change this SQL query to get the desired number (7, not 77)?
just take the aggregate sum first on t2, then join with t2 like this:
SELECT t1.TripID, COUNT(t1.SampleID) AS Samples, SUM(t3.Bugs) as Bugs
FROM tbl_Sample AS t1
LEFT Join (
SELECT t2.SampleID, SUM(t2.C1 + t2.C2) as Bugs
FROM tbl_Bugs as t2
GROUP BY SampleID) AS t3 ON t1.SampleID = t3.SampleID
GROUP BY t1.TripID
This is a tricky query, because you have different hierarchies. Here is one method:
select s.tripid, count(*) as numsamples,
(select sum(b2.c1 + b2.c2)
from bugs b join
tbl_sample s2
on s2.sampleid = b.sampleid
where s2.tripid = s.tripid
) as numbugs
from tbl_sample s
group by s.tripid
You included a DISTINCT with a Group By. This is removing duplicates twice, which is unnecessarily complex. You can get rid of the DISTINCT.
I would have the count separate from what is going on in the group by.
SELECT dT.TripID
,(SELECT COUNT(DISTINCT(SampleID))
FROM Bugs B
WHERE B.TripID = dT.TripID
) AS [Samples]
,dT.Bugs
FROM (
SELECT t1.TripID
,SUM(t2.C1 + t2.C2) AS Bugs
FROM tbl_Sample AS t1
LEFT JOIN tbl_Bugs AS t2 ON t1.SampleID = t2.SampleID
GROUP BY t1.TripID
) AS dT

How to combine those three SQL queries

How to combine this three queries without giving me the same output ?
The first query is :
select
vwemployee.directorateName,
count(vwemployeeCourse.employeeId) as t1
from
vwemployee, vwemployeeCourse
where
vwemployee.directorateName = vwemployeeCourse.directorateName
group by
vwemployee.directorateName
This is the second query :
select
vwemployee.directorateName,
count(vwemployee.directorateName) as t2
from
vwemployee, employeeCourse
where
vwemployee.Id = employeeCourse.employeeId
group by
vwemployee.directorateName
This is the third query :
select
vwemployeeCourse.directorateName, sum(vwCourse.cost) as t3
from
vwemployeeCourse, vwCourse
where
vwemployeeCourse.courseId = vwCourse.Id
group by
vwemployeeCourse.directorateName
I will be using the combined query to generate a report
the t1 column should display how many courses this specific directorate took
the t2 column should display how many employee's under this directorate took this courses
the t3 column should display how much the courses cost for every directorate
So the total columns of the table of the combined query should be 4 columns
FYI: some nice people here helped me to combine the first two queries but it was not sample at all and i didn't succeed to add the third query to them since I am a beginner so please help me with a full simple query to understand it for future references
I think you are looking for something like this. We can achieve this using NESTED CTEs. You can see here I have created 3 nested CTEs, and in the end I have used all the three CTEs to get your result.
with cte1 as
(
select vwemployee.directorateName , count(vwemployeeCourse.employeeId) as t1
from vwemployee , vwemployeeCourse
where vwemployee.directorateName = vwemployeeCourse.directorateName
GROUP BY vwemployee.directorateName
)
,cte2 as
(
select vwemployee.directorateName , count(vwemployee.directorateName) as t2
from vwemployee , employeeCourse
where vwemployee.Id = employeeCourse.employeeId
GROUP BY vwemployee.directorateName
)
,cte3 as
(
select vwemployeeCourse.directorateName , sum(vwCourse.cost) as t3
from vwemployeeCourse , vwCourse
where vwemployeeCourse.courseId = vwCourse.Id
group by vwemployeeCourse.directorateName
)
select cte1.directorateName, cte1.t1, cte2.t2, cte3.t3
from
cte1 inner join cte2
on cte1.directorateName = cte2.directorateName
inner join cte3 on
cte2.directorateName = cte3.directorateName
First, you should be using explicit JOIN syntax. Simple rule: Never use commas in the FROM clause.
Then, given the three queries as written, with no other information, I think I would go for full outer join or union all with aggregation:
with ec as (
select e.directorateName, count(ec.employeeId) as t1
from vwemployee e join
vwemployeeCourse ec
on e.directorateName = ec.directorateName
group by e.directorateName
),
ed as (
select e.directorateName, count(ec.directorateName) as t2
from vwemployee e join
vwemployeeCourse ec
on e.id = ec. employeeId
group by e.directorateName
),
cc as (
select ec.directorateName, sum(c.cost) as t3
from vwemployeeCourse ec join
vwCourse c
ec.courseId = c.Id
group by ec.directorateName
)
select directoratename,
coalesce(t1, 0) as t1,
coalesce(t2, 0) as t2,
coalesce(t3, 0) as t3
from ((select directoratename, t1, null as t2, null as t3 from ec)
union all
(select directoratename, null as t1, t2, null as t3 from ed)
union all
(select directoratename, null as t1, null as t2, t3 from cc)
) t;
Having said that, I don't think this query can actually do anything useful. Joining two tables on two different keys, and then aggregating by the same key (as for ec and ed) is not usually done. If this doesn't produce the results you want, then ask another question, provide sample data, desired results, and a SQL Fiddle.

selecting records from main table and count of each row in another table

I have 2 table in my database that tables are in relationship with foreign key
I want to select all records from main table and then select count of each row in another table than have same ID from main table I tried to create a select query but it is not work correctly
this query return all records from main table + count of all records from next table(not count of each row in relationship)
SELECT tblForumSubGroups_1.id, tblForumSubGroups_1.GroupID,
tblForumSubGroups_1.SubGroupTitle, tblForumSubGroups_1.SubGroupDesc,
(SELECT COUNT(dbo.tblForumPosts.id) AS Expr1
FROM dbo.tblForumSubGroups INNER JOIN dbo.tblForumPosts ON
dbo.tblForumSubGroups.id = dbo.tblForumPosts.SubGroupID) AS Expr1
FROM dbo.tblForumSubGroups AS tblForumSubGroups_1 INNER JOIN
dbo.tblForumPosts AS tblForumPosts_1 ON tblForumSubGroups_1.id
= tblForumPosts_1.SubGroupID
SELECT tblForumSubGroups_1.id, tblForumSubGroups_1.GroupID, tblForumSubGroups_1.SubGroupTitle, tblForumSubGroups_1.SubGroupDesc,
COUNT(tblForumPosts_1.id) AS Expr1
FROM dbo.tblForumSubGroups AS tblForumSubGroups_1
INNER JOIN dbo.tblForumPosts AS tblForumPosts_1 ON tblForumSubGroups_1.id = tblForumPosts_1.SubGroupID
GROUP BY tblForumSubGroups_1.id, tblForumSubGroups_1.GroupID, tblForumSubGroups_1.SubGroupTitle, tblForumSubGroups_1.SubGroupDesc
I would suggest cross apply as you can do a lot more things with it ...
SELECT t1.id,
t1.GroupID,
t1.SubGroupTitle,
t1.SubGroupDesc,
t2.val
FROM dbo.tblForumSubGroups AS t1
cross apply (SELECT COUNT(*)
FROM dbo.tblForumPosts as t2
WHERE t1.id = t2.SubGroupID) x(val)
Do not mix sub-query and join logic. Use only one of them. I prefer sub-select.
SELECT tblForumSubGroups_1.id,
tblForumSubGroups_1.GroupID,
tblForumSubGroups_1.SubGroupTitle,
tblForumSubGroups_1.SubGroupDesc,
(SELECT COUNT(*)
FROM dbo.tblForumPosts
WHERE dbo.tblForumSubGroups.id = dbo.tblForumPosts.SubGroupID) AS Expr1
FROM dbo.tblForumSubGroups AS tblForumSubGroups_1
Just to supply another answer though I believe the cross apply is likely the best option:
SELECT
A.id, A.GroupID, A.SubGroupTitle, A.SubGroupDesc,
B.IDCount AS Expr1
FROM dbo.tblForumSubGroups A
INNER JOIN (
Select SubGroupID, Count(ID) as IDCount
from dbo.tblForumPosts
Group By SubGroupID
) B On A.ID = B.SubGroupID

Exposing more fields on group by sql

I know, in a Group By you can't Select a field that is not in an aggregate function or the GROUP BY clause.
However, There must be a workaround using joins or something else.
I have TWO tables BMP_VISITS_SITES and BMP_VISITS_COMMENTS which are connected by StationID in a one-to-many relationship. One Site can have many comments.
I'm trying to write a query that returns all Sites and the latest (only 1) comment. I have a "working" query but it only returns two columns which are in either an aggregate function or group by.
Here is my "working" query:
select a.StationID,
MAX(b.[dateobserved]) as LastDateObserved,
a.Status
from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
group by a.StationID;
But how can I access all the columns in both tables?
I've tried inner joins with 1/2 success. When I join my BMP_VISITS_SITES to the above query I get all the fields of the table (t1). Great, but as soon as I try joining on BMP_VISITS_COMMENTS (t3) I get more results than I should.
select t1.*, t2.*
--,t3.*
from BMP_VISITS_SITES t1
inner join (
select a.StationID, MAX(b.[dateobserved]) as LastDateObserved from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
group by a.StationID
) t2 on t2.StationID = t1.StationID
--inner join sde.BMP_VISITS_COMMENTS t3 on t3.StationID = t2.StationID;
SELECT a.*, b.* FROM
BMP_VISITS_SITES a
OUTER APPLY
(
SELECT TOP 1 *
FROM BMP_VISITS_COMMENTS b
WHERE b.StationID = a.StationID
ORDER BY LastDateObserved DESC
) b
You can use apply to get the last comment record and return all fields from both sides of the query.
Use row_number()
select *
from
(
select a.StationID,
a.Status,
b.*,
row_number() over (partition by a.stationid, a.status order by b.[dateobserved] desc) as rn
from BMP_VISITS_SITES a
left outer join BMP_VISITS_COMMENTS as b
on a.[StationID] = b.[StationID]
) v
where rn = 1