Check if relation exists and return true or false - sql

I have 3 tables, Category Step and CategoryStep, where CategoryStep relates the two other tables together. I want to return all categories with a true/false column whether or not the relation exists in CategoryStep based on a StepID.
The schema for the tables is simple,
Category:
CategoryID | CategoryName
Step:
StepID | StepName
CategoryStep:
CategoryStepID | CategoryID | StepID
When trying to get results based on StepID, I only get the relations that exist, and not ones that don't.
SELECT [CategoryID], [Category], CAST(CASE WHEN [CategoryStep].[CategoryStep] IS NULL THEN 0 ELSE 1 END AS BIT) AS related
FROM Category
LEFT JOIN CategoryStep ON Category.CategoryID = CategoryStep.CategoryID
INNER JOIN Step ON CategoryStep.StepID = Step.StepID
WHERE Step.StepID = 2
Step Table:
|StepID | StepName
|-------|---------
| 1 | StepOne
| 2 | StepTwo
| 3 | StepThree
Category Table:
| CategoryID | CategoryName
|------------|-------------
| 1 | Holidays
| 2 | States
| 3 | Cities
| 4 | Animals
| 5 | Food
CategoryStep Table
| CategoryStepID | CategoryID | StepID
|----------------|------------|-------
| 1 | 1 | 1
| 2 | 1 | 2 <--
| 3 | 2 | 1
| 4 | 2 | 3
| 5 | 3 | 2 <--
| 6 | 4 | 1
| 7 | 4 | 2 <--
| 8 | 4 | 3
| 9 | 5 | 1
| 10 | 5 | 3
So, if I was looking for StepID = 2 the result table I am looking for is:
| CategoryID | Category | Related
|------------|----------|--------
| 1 | Holidays | 1
| 2 | States | 0
| 3 | Cities | 1
| 4 | Animals | 1
| 5 | Food | 0

Try replacing the INNER JOIN with a LEFT JOIN.
Update:
The fatal flaw with your original attempt was the WHERE clause. You were performing the correct LEFT JOIN, but the WHERE clause was filtering off category records which did not match. In the query below, I moved the check for step ID into the join condition, where it belongs.
SELECT [CategoryID], [Category],
CAST(CASE WHEN [CategoryStep].[CategoryStep] IS NULL THEN 0 ELSE 1 END AS BIT) AS related
FROM Category
LEFT JOIN CategoryStep
ON Category.CategoryID = CategoryStep.CategoryID AND
CategoryStep.StepCodeID = 2
LEFT JOIN Step
ON CategoryStep.StepID = Step.StepID

Related

How to insert records based on another table value

I have the following three tables:
Permission
| PermissionId | PermissionName |
+--------------+----------------+
| 1 | A |
| 2 | B |
| 3 | C |
| 100 | D |
Group
| GroupId | GroupLevel | GroupName |
+---------+------------+----------------------+
| 1 | 0 | System Administrator |
| 7 | 0 | Test Group 100 |
| 8 | 20 | Test Group 200 |
| 9 | 20 | test |
| 10 | 50 | TestGroup01 |
| 11 | 51 | TestUser02 |
| 12 | 52 | TestUser03 |
GroupPermission
| GroupPermissionId | FkGroupId | FkPermissionId |
+-------------------+-----------+----------------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 1 | 4 |
I need to insert records into GroupPermission table, if table Group, GroupLevel column have 0
then I need to take its GroupId and need to insert values to GroupPermission table as that particular id and 100.
In order to above sample table records, I need to insert the following two records to GroupPermission table,
| FkGroupId | FkPermissionId |
+-----------+----------------+
| 1 | 100 |
| 7 | 100 |
How can I do it
This question is not very clear and I can only assume the value 100 is a static value and that you don't actually have foreign keys as the names of the columns imply. Also, you really should avoid reserved words like "Group" for object names. It makes things more difficult and confusing.
The simple version of your insert might look like this.
insert GroupPermission
(
FkGroupId
, FkPermissionId
)
select g.GroupId
, 100
from [Group] g
where g.GroupLevel = 0
--EDIT--
Since you want to only insert those rows that don't already exist you can use NOT EXISTS like this.
select g.GroupId
, 100
from [Group] g
where g.GroupLevel = 0
AND NOT EXISTS
(
select *
from GroupPermission gp
where gp.FkGroupId = g.GroupId
and g.FkPermissionId = 100
)
Or you could use a left join like this.
select g.GroupId
, 100
from [Group] g
left join GroupPermission gp on gp.FkGroupId = g.GroupId
and gp.FkPermissionId = 100
where g.GroupLevel = 0
and gp.FkGroupId is null

Only include grouped observations where event order is valid

I have a table of dates for eye exams and eye wear purchases for individuals. I only want to keep instances where individuals bought their eye wear following an eye exam. In the example below, I would want to keep person 1, events 2 and 3 for person 2, person 3, but not person 4. How can I do this in SQL server?
| Person | Event | Order |
| 1 | Exam | 1 |
| 1 | Eyewear| 2 |
| 2 | Eyewear| 1 |
| 2 | Exam | 2 |
| 2 | Eyewear| 3 |
| 3 | Exam | 1 |
| 3 | Eyewear| 2 |
| 4 | Eyewear| 1 |
| 4 | Exam | 2 |
The final result would look like
| Person | Event | Order |
| 1 | Exam | 1 |
| 1 | Eyewear| 2 |
| 2 | Exam | 2 |
| 2 | Eyewear| 3 |
| 3 | Exam | 1 |
| 3 | Eyewear| 2 |
Self join should work...
select
t.Person
,t.Event
,t.[Order]
from
yourTable t
inner join
yourTable t2 on t2.Person = t.Person
and t2.[Order] = (t.[Order] +1)
where
t2.Event = 'Eyewear'
and t.Event = 'Exam'
I haven't tried to optimize it but this seems to work:
create table t(
person varchar(10),
event varchar(10),
[order] varchar(10)
);
insert into t values
('1','Exam','1'),
('1','Eyewear','2'),
('2','Eyewear','1'),
('2','Exam','2'),
('2','Eyewear','3'),
('3','Exam','1'),
('3','Eyewear','2'),
('4','Eyewear','1'),
('4','Exam','2');
with xxx(person,event_a,seq_a,event_b,seq_b) as (
select a.person,a.event,a.[order],b.event,b.[order]
from t a join t b
on a.person = b.person
and a.[order] < b.[order]
and a.event like 'exam'
and b.event like 'eyewear'
)
select person,event_a event,seq_a [order] from xxx
union
select person,event_b event,seq_b [order] from xxx
order by 1,3

Counting on multiple columns

I have a table like this:
+------------+---------------+-------------+
|store_number|entrance_number|camera_number|
+------------+---------------+-------------+
| 1 | 1 | 1 |
| 1 | 1 | 2 |
| 2 | 1 | 1 |
| 2 | 2 | 1 |
| 2 | 2 | 2 |
| 3 | 1 | 1 |
| 4 | 1 | 1 |
| 4 | 1 | 2 |
| 4 | 2 | 1 |
| 4 | 3 | 1 |
+------------+---------------+-------------+
In summary the stores are numbered 1 and up, the entrances are numbered 1 and up for each store, and the cameras are numbered 1 and up for each entrance.
What I want to do is count how many how many entrances in total, and how many cameras in total for each store. Producing this result from the above table:
+------------+---------------+-------------+
|store_number|entrances |cameras |
+------------+---------------+-------------+
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 1 | 1 |
| 4 | 3 | 4 |
+------------+---------------+-------------+
How can I count on multiple columns to produce this result?
You can do this with a GROUP BY and a COUNT() of each item:
Select Store_Number,
Count(Distinct Entrance_Number) as Entrances,
Count(Camera_Number) As Cameras
From YourTable
Group By Store_Number
From what I can tell from your expected output, you're looking for the number of cameras that appear, whilst also looking for the DISTINCT number of entrances.
This will work as well,
DECLARE #store TABLE
( store_number INT,entrance_number INT,camera_number INT)
INSERT INTO #store VALUES(1,1,1),(1,1,2),(2,1,1),(2,2,1),
(2,2,2),(3,1,1),(4,1,1),(4,1,2),(4,2,1),(4,3,1)
SELECT AA.s store_number, BB.e entrances,AA.c cameras FROM (
SELECT s,COUNT(DISTINCT c) c FROM ( SELECT store_number s,
CONVERT(VARCHAR,store_number) + CONVERT(VARCHAR,entrance_number) +
CONVERT(VARCHAR,camera_number) c FROM #store ) A GROUP BY s ) AA
LEFT JOIN
( SELECT s,COUNT(DISTINCT e) e FROM ( SELECT store_number s,
CONVERT(VARCHAR,store_number) + CONVERT(VARCHAR,entrance_number) e
FROM #store ) B GROUP BY s ) BB ON AA.s = BB.s
Hope it helped. :)

CREATE VIEW with multiple tables - must show 0 values

I have three tables:
// priorities // statuses // projects
+----+--------+ +----+-------------+ +----+------+--------+----------+
| ID | NAME | | ID | STATUS NAME | | ID | NAME | STATUS | PRIORITY |
+----+--------+ +----+-------------+ +----+------+--------+----------+
| 1 | Normal | | 1 | Pending | | 1 | a | 1 | 3 |
+----+--------+ +----+-------------+ +----+------+--------+----------+
| 2 | High | | 2 | In Progress | | 2 | b | 1 | 1 |
+----+--------+ +----+-------------+ +----+------+--------+----------+
| 3 | Urgent | | 3 | c | 2 | 1 |
+----+--------+ +----+------+--------+----------+
| 4 | d | 1 | 2 |
+----+------+--------+----------+
I need to create a view that shows how many projects hold a status of 1 and a priority of 1, how many hold a status of 1 and a priority of 2, how many hold a status of 1 and a priority of 3, and so on.
This should go through each status, then each priority, then count the projects that apply to the criteria.
The view should hold values something like this:
// VIEW (stats)
+--------+----------+-------+
| STATUS | PRIORITY | COUNT |
+--------+----------+-------+
| 1 | 1 | 1 |
+--------+----------+-------+
| 1 | 2 | 1 |
+--------+----------+-------+
| 1 | 3 | 1 |
+--------+----------+-------+
| 2 | 1 | 1 |
+--------+----------+-------+
| 2 | 2 | 0 |
+--------+----------+-------+
| 2 | 3 | 0 |
+--------+----------+-------+
This view is so that I can call, for example, how many projects have a status of 1 and a priority of 3, the answer given the data above should be 1.
Using the below select statement I've been able to produce a similar result but it does not explicitly show that 0 projects have a status of 2 and a priority of 3. I need this 0 value to be accessible the same way as any of the others with a COUNT >= 1.
// my current select statement
CREATE VIEW stats
AS
SELECT P.STATUS, P.PRIORITY, COUNT(*) AS hits
FROM projects P
GROUP BY P.STATUS, P.PRIORITY
// does not show rows where COUNT = 0
How could I create a VIEW that holds all of the priorities' ids, all of the statuses' ids, and 0 values for COUNT?
You need to generate all the rows and then get the count for each one. Here is a query that should work:
SELECT s.status, p.priority, COUNT(pr.status) AS hits
FROM (SELECT DISTINCT status FROM projects) s CROSS JOIN
(SELECT DISTINCT priority FROM projects) p LEFT JOIN
project pr
ON pr.status = s.status and pr.priority = p.priority
GROUP BY s.status, p.priority;

Can't figure out a simple SQL query

Might be very simple, but I've been digging fow a few days now... I just can't figure out how to make this SQL query in Access...
In reference to the tables below, i'm looking for the query that can extract all the ITEMS for a specific Shop (ie 1:Alpha) from a specific GROUP (ie 1:Tools), that are NOT in the report for 2014... in this case ITEMS.IDs 6, 8, 9 and 10!
Tables:
Years
ID | Year
-----------------------------------------------
1 | 2014
2 | 2015
Shops
ID | ShopName
-----------------------------------------------
1 | Alpha
2 | Bravo
Items
ID | StockNbr | Description | GroupID
-----------------------------------------------
1 | 00-1200 | Ratchet 1/4 | 1
2 | 00-1201 | Ratchet 1/2 | 1
3 | 00-1300 | Screwdriver Philips No1 | 1
4 | 01-5544 | Banana | 2
5 | 00-4457 | Apple | 2
6 | 21-8887 | Hammer | 1
7 | 21-6585 | Drill | 1
8 | 21-4499 | Multimeter | 1
9 | 21-5687 | Digital Caliper | 1
10 | 22-7319 | File Set | 1
...
Groups
ID | GroupName
-----------------------------------------------
1 | Tools
2 | Fruits
REPORTS
ID | YearID | ShopID | ItemID
-----------------------------------------------
1 | 1 | 1 | 1
2 | 1 | 1 | 2
3 | 1 | 1 | 3
4 | 1 | 1 | 4
5 | 1 | 1 | 7
6 | 1 | 2 | 5
7 | 1 | 2 | 8
8 | 1 | 2 | 10
I've tried this, but then I realize it doesn't take the shops into consideration, it'll list all items that are not listed in reports, so if reports has an item for shop 2, it won't list it either...
SELECT Items.ID, Items.StockNbr, Items.Description, Items.GroupID, Reports.YearID, Reports.ShopID
FROM Reports
RIGHT JOIN Items ON Reports.ItemID = Items.ID
WHERE (((Items.GroupID)=1) AND ((Reports.UnitID) Is Null))
ORDER BY Items.StockNbr;
Thank you!
I think you're looking for an anti-join. There are several ways to do this. Here's one using not in.
select i.* from items i
where i.GroupId = 1
and i.ID NOT IN (
select ItemID from reports r
where r.ShopID = 1
and r.YearID = 2014
)
If the table Reports does not reference Items.ID then there is no available relationship ShopID or YearID
select *
from items
left join reports on items.id = reports.itemid
where reports.itemid IS NULL