Relational Division Query - tsql

I Have tree table in SQL Server 2008 likes below:
UserTbl:
UserID UserName
1 User1
2 User2
3 User3
ActivityTbl:
ActivityID Activity
1 A
2 B
3 C
UserAccessTbl (Relational Division Table):
UserID Activity Access
1 A True
1 B False
2 C True
I want to have a query with this result:
UserID Activity Access
1 A True
1 B False
1 C Null
2 A Null
2 B Null
2 C True
3 A Null
3 B Null
3 C Null
Would you please help me to do that?
I want to use this to perform users access setting in my .Net application.
Tanks

Join UserTbl with ActivityTbl using a cross join
Select * from UsertTbl cross join ActivityTbl
https://technet.microsoft.com/en-us/library/ms190690%28v=sql.105%29.aspx
This will result in a dataset with all the possible combinations between users and activities.
Join the previous dataset with UserAccessTbl using "left join".
https://technet.microsoft.com/en-us/library/ms187518%28v=sql.105%29.aspx
(NOTE: you can do it in only one query, just join the two pieces)

Related

Count after filtering/joining two tables

I think I am missing something obvious or otherwise on the wrong path here. I am using postgresql (new to it).
I have two tables:
TABLE A:
id
age
1
20
2
55
3
65
4
75
5
85
TABLE B:
id
service1
service2
1
Yes
Yes
2
Yes
No
3
Yes
Yes
4
Yes
Yes
5
No
Yes
I want to get the count of all customers over the age of 55 with service1 and service2.
When I use the code below, I get the correct list of customers, but doing a select count(*) does not give me a total, but rather count per each id.
SELECT *
FROM A
INNER JOIN B on A.id = B.id
WHERE A.age > 54
AND B.service1 = 'Yes'
AND B.service2 = 'Yes'
GROUP BY A.id, B.id;
I am looking for a total count but I end up with something like this:
count
1
1
2
1
I am sure this has been answered many times but I am having a hard time searching and finding it. I am new to this, so my google skills are not up to par yet. Thank you!
One canonical approach here uses aggregation over the entire joined tables:
SELECT COUNT(*)
FROM A a
INNER JOIN B b
ON b.id = a.id
WHERE b.service1 = 'Yes' AND b.service2 = 'Yes' AND
a.age > 55;

I want to get unique rows on joining tables

I am running the following query while trying to join 3 tables :
select
a.project_id, a.acc_name, a.project_name, a.iot,a.acc_id, a.active,
b.app_fte, b.contact_person, c.cost_call_date
from
Account a, Application b, account_version c
where
a.acc_id in (Select acc_id from account where acc_name='GGG') and
EXTRACT(MONTH FROM c.cost_call_date) = 3;
Sample data from the tables are as follows :
Account :
acc_id acc_name iot acc_contact project_id project_name ilc_code license_no active
2 GGG NA YYY 7777 HHH TTR 766 false
Application :
app_id app_name app_fte contact_person acc_id
1 sfsf 4 sdsdff 2
Account_version :
line_id acc_id version_no chargable_fte cost_call_date is_approved
9 2 7 4 2018-03-20
Here acc_id is the primary key for the Account table and the foreign key for the Application and Account_version tables. When I am running the above query I am getting 30 rows I have also tried using the distinct keyword but still I get 10 rows. Please help me in getting unique rows.
Try something like this
SELECT DISTINCT a.project_id, a.acc_name, a.project_name, a.iot,a.acc_id, a.active, b.app_fte, b.contact_person, c.cost_call_date
FROM Account a
INNER JOIN Application b
USING (acc_id)
INNER JOIN account_version c
USING (acc_id)
WHERE a.acc_name = 'GGG'
AND EXTRACT(MONTH FROM c.cost_call_date) = 3
For reference as to why your query was giving you more rows than expected, try running this:
SELECT a.*, b.*
FROM generate_series(1, 10) a, generate_series(1, 10) b
What you are doing by selecting from multiple tables as you did is a cross join. What you should actually be doing is an inner join to get the rows you want, and a DISTINCT if required to get only distinct rows from the results.

Limit for inner Join Table

I have a scenario where I am joining three tables and getting the results.
My problem is i have apply limit for joined table.
Take below example, i have three tables 1) books and 2) Customer 3)author. I need to find list of books sold today with author and customer name however i just need last nth customers not all by passing books Id
Books Customer Authors
--------------- ---------------------- -------------
Id Name AID Id BID Name Date AID Name
1 1 1 ABC 1 A1
2 2 1 CED 2 A2
3 3 2 DFG
How we can achieve this?
You are looking for LATERAL.
Sample:
SELECT B.Id, C.Name
FROM Books B,
LATERAL (SELECT * FROM Customer WHERE B.ID=C.BID ORDER BY ID DESC LIMIT N) C
WHERE B.ID = ANY(ids)
AND Date=Current_date

How to sum items from subtable in SQL

Let's say I have table orders
id name
1 order1
2 order2
3 order3
and subtable items
id parent amount price
1 1 1 10
2 1 3 20
3 2 2 5
4 2 5 1
I would like to create query with order with added column value. it should calculate order with all relevant items
id name value
1 order1 70
2 order2 15
3 order3 0
Is this possible with TSQL
GROUP BY and SUM would do it, need to use left join and isnull as you don't have items for all orders.
SELECT o.id, o.name, isnull(sum(i.amount*i.price),0) as value
FROM orders o
left join items i
on o.id = i.parent
group by o.id, o.name
I think you're looking for something like this
SELECT o.name, i.Value FROM orders o WITH (NOLOCK)
LEFT JOIN (SELECT parent, SUM(price) AS Value FROM items WITH (NOLOCK) GROUP BY parent) i
ON o.id = i.parent
...seems like RADAR beat me to the answer.
EDIT: missing the ON line.

How to determine whether a value exists in a junction table and return zero or one?

I am using SQL Server 2008 R2
I am trying to write a single query that will return only exactly what I need. I will drop in a MovieID and get back a list of ALL genres. If the movie represents a specific genre (has an associated record in the junction table), the Checked value will be 1. If not, then 0.
My result set should look like this:
GenreID Genre Checked
1 ABC 0
2 DEF 1
3 HIJ 0
4 KLM 1
My First table is named Genres. It looks like this:
GenreID Genre
1 ABC
2 DEF
3 HIJ
4 KLM
My second table is named Movies. It looks like this:
MovieID Title
1 Blah
2 Foo
3 Carpe
4 Diem
My third table is a junction table named Movies_Genres. It looks like this:
MovieID GenreID
1 2
1 1
1 4
2 1
2 3
3 4
4 1
I would normally, do a couple of queries and a couple of loops to handle this, but I want to really just make the database do the work here. How do I tweak my query so that I can get the resultset that I need with just a single query?
Here's the starting query:
SELECT GenreID,
Genre
FROM Genres
Thanks in advance for your help!!!
SELECT g.GenreID, g.Genre, Checked = CASE WHEN EXISTS
(SELECT 1 FROM dbo.Movies_Genres AS mg
INNER JOIN dbo.Movies AS m
ON mg.MovieID = m.MovieID
WHERE mg.GenreID = g.GenreID
AND m.MovieID = #MovieID) THEN 1 ELSE 0 END
FROM dbo.Genres AS g
ORDER BY g.GenreID;
If there is a unique constraint or primary key on dbo.Movies_Genres(MovieID, GenreID) then this can be simply:
SELECT g.GenreID, g.Genre, Checked = COUNT(mg.GenreID)
FROM dbo.Genres AS g
LEFT OUTER JOIN dbo.Movies_Genres AS mg
ON g.GenreID = mg.GenreID
AND mg.MovieID = #MovieID
GROUP BY g.GenreID, g.Genre;
...since the count for any genre can only be 0 or 1 given a single #MovieID.
Pretty straight forward using CASE;
SELECT DISTINCT g.GenreID, g.Genre,
CASE WHEN mg.MovieID IS NULL THEN 0 ELSE 1 END Checked
FROM Genres g
LEFT JOIN Movies_Genres mg
ON g.GenreID=mg.GenreID
AND mg.MovieId=#MovieID;
Demo here.
Edit: If entries are guaranteed to be unique in Movies_Genres, you could choose to drop the DISTINCT.
The #MovieID is the movie, you want to filter by.
SELECT Genres.GenreID,
Genres.Genre,
CASE WHEN (Movies_Genres.GenreID IS NULL)
THEN 0
ELSE 1
END AS Checked
FROM Genres LEFT JOIN
Movies_Genres ON Movies_Genres.GenreID = Genres.GenreID AND
MovieID = #MovieID