Inserting into a join table if value does not already exist - sql

I have 2 tables - user, region and a join/connecting table that is used to join both user and region.
I need to insert into the join table all the region values that the user does not already have and i am unsure how to go about this.
I have attempted this numerous ways but i am not entirely sure how to only place the values that do already exist within the table into the join table. Does anyone have any ideas or suggestions?
SELECT
CONVERT( CONCAT('INSERT INTO user_region VALUES(',
user.id,
',',
reg.id,
');') USING UTF8)
FROM
user user
JOIN
user_region user_reg ON user_reg.id = user.id
JOIN
region reg ON reg.id = user_reg.id
WHERE
(user.email_address LIKE '%gmail%'
OR user.email_address LIKE '%hotmail%');
User Table User Region Region
----------- ----------- ------
1 1 2 1
2 3 2 2
3 3 4 3
4 4 3 4

Kind of
INSERT INTO user_region (userID, regionID)
SELECT u.userID, r.regionID
FROM
(SELECT DISTINCT userId
FROM user
WHERE user.email_address LIKE '%gmail%'
OR user.email_address LIKE '%hotmail%') u
JOIN region r ON NOT EXISTS (
SELECT 1
FROM user_region ur
WHERE ur.userID = u.userID AND ur.regionID = r.regionID )

Related

Join a table with another table with columns containing null

I want to join users table with both Groupid and superadmingroupid from group table.
Superadmingroupid may be null
I tried below query but not working
SELECT U.Name, G.Name
FROM Groups G
INNER JOIN USERS U ON G.Groupid = U.Gid
LEFT JOIN USERs U2 On G.superadmingroupid= U.Gid
where U.Name='Mishrsa'
Group table
Groupid Gname SuperAdminGroupId
----- ------ --------
17 A 3
2 B null
3 C null
Users
------
id Name Gid
-- ------- ----
1 mishra 2
2 mishrsa 3
I want to diplay the user with groups that are referenced as groupid or superadmingroupid
Ex: User does not have groupid 17 but superadmingroupid 3 is there in users table so group 17 should come in the output
Output
Name GName
Mishra B
Mishra C
Mishra A
Solution for your problem is:
SELECT U.Name, G.GName
FROM Groups G
INNER JOIN USERS U
ON G.Groupid = U.Gid
OR G.superadmingroupid= U.Gid;
Working example: dbfiddle Link
I believe you should use UNION for that. (Maybe this is not the most elegant way).
The first part will give you the match between Groupid to Gid.
The second part will give you the match between SuperAdminGroupId to Gid.
The order is different then what you mentioned, and I do not know if it is important for you, but please try the below example:
SELECT U.Name, G.Name
FROM Groups G
JOIN Users U ON G.Groupid = U.Gid
UNION
SELECT U.Name, G.Name
FROM Groups G
JOIN Users U ON G.SuperAdminGroupId = U.Gid
Posting this answer just because I'd already written it before Bogner Boy posted their answer.
I changed the table names a touch because GROUP is a reserved word in SQL Server.
Bonger Boy's UNION might be more efficient for larger tables, but for smaller tables you'll be fine to use an OR or an IN:
CREATE TABLE AdminGroup
(
GroupId INTEGER,
Gname CHAR(1),
SuperAdminGroupId INTEGER
);
CREATE TABLE Users
(
Id INTEGER,
Name NVARCHAR(64),
Gid INTEGER
);
INSERT INTO Users (Id, Name, Gid) VALUES (1, 'mishra', 2);
INSERT INTO Users (Id, Name, Gid) VALUES (2, 'mishra',3);
INSERT INTO AdminGroup (GroupId, Gname, SuperAdminGroupId) VALUES (17, 'A', 3);
INSERT INTO AdminGroup (GroupId, Gname, SuperAdminGroupId) VALUES (2, 'B', null);
INSERT INTO AdminGroup (GroupId, Gname, SuperAdminGroupId) VALUES (3, 'C', null);
SELECT U.Name, G.GName
FROM Users U
INNER JOIN AdminGroup G ON U.Gid = G.SuperAdminGroupId OR U.Gid = G.GroupId;
--INNER JOIN AdminGroup G ON U.Gid IN (G.SuperAdminGroupId, G.GroupId);
Here's a DBFiddle:
https://www.db-fiddle.com/f/p1RA4z67SH1DijFMZyKRuA/0

SQL Get records with max value for each group

I have 2 tables Journal and Users
Journal looks like this:
TransTime
RegNumber
UserID
5/26/2022 11:00:00
101
3
5/26/2022 11:30:00
102
2
5/26/2022 13:00:00
101
5
5/26/2022 14:30:00
103
4
5/26/2022 15:00:00
102
1
Users table
UserID
Name
1
Ross
2
Rachel
3
Chandler
4
Monica
5
Joey
What I would like to do is get a table of the Registers and their most recent user names. This should seem very simple. But since I am joining tables on the userID, I am getting all 5 records on the first table. But it should look like this:
RegNumber
LastUser
101
Joey
102
Ross
103
Monica
I have tried a variety of solutions but haven't found the right one. Any help is appreciated.
You can use a temptable or cte structure to rank your data based on RegNo and Trantime like below, then retrieve the most updated users for each journal:
CREATE TABLE #Journals (TranTime DATETIME, RegNo INT, UserId INT)
CREATE TABLE #Users (UserId INT, UserName NVARCHAR(100))
INSERT INTO #Users VALUES(1,'Ross'),(2,'Rachel'),(3,'Chandler'),(4,'Monica'),(5,'Joey')
INSERT INTO #Journals VALUES ('5/26/2022 11:00:00',101,3),('5/26/2022 11:30:00',102,2),
('5/26/2022 13:00:00',101,5),('5/26/2022 14:00:00',103,4),('5/26/2022 15:00:00',102,1)
;WITH cte as (
SELECT *,rn=ROW_NUMBER() OVER (PARTITION BY RegNo ORDER BY TranTime DESC)
FROM #Journals
)
SELECT RegNo, u.UserName
FROM cte
INNER JOIN #Users u ON u.UserId = cte.UserId
WHERE rn=1 --since sort by TranTime is descending, it'll give you the latest user for each specific RegNo
ORDER BY RegNo
Tested and it works on SQL Server 2016.
if you start with an inner join of the max transtime per regNumber, then join with user table:
Select J.RegNumber, U.Name
From Journal J
Inner join
(Select Max(TransTime) as TransTime, RegNumber
From Journal
Group by RegNumber) J2 on J.TransTime = J2.TransTime and J.RegNumber = J2.RegNumber
Inner join
Users U on J.UserID = U.UserID
Here is an option using a CTE:
;with cte as
(
Select RegNumber,
UserID = max(UserID)
From journal
group by RegNumber
)
Select RegNumber = C.RegNumber,
LastUser = U.Name
From cte C
Join users U ON U.Userid = C.UserID
order by C.RegNumber
This answer is not, at its core, substantively different from the others. However, in terms of being helpful to the target audience it's more readable, more self-documenting, and more standard in terms of formatting.
Sidebar: This SQL takes the data design at face value, as a given, with the implicit assumption that TransTime is the PK or at least uniquely indexed, possibly in conjunction with RegNumber. Bottom line, it would be good to have a little more info about the key structure along with the original question.
WITH LatestEntries AS
(
SELECT
MAX(TransTime) AS LatestTimeForReg
,RegNumber
FROM
Journal
GROUP BY
RegNumber
)
SELECT
J.RegNumber
,U.[Name] AS LastUser
FROM
LatestEntries LE
INNER JOIN Journal J ON LE.LatestTimeForReg = J.TransTime AND LE.RegNumber = J.RegNumber
INNER JOIN Users U ON J.UserID = U.UserID
ORDER BY
J.RegNumber
;
select u.Name, j.*
from journal j
inner join (
select max(TransTime) last_update, RegNumber
from journal
group by RegNumber
) t1
inner join j.RegNumber = t1.RegNumber
and t1.last_update = j.TransTime
left join Users_Journal uj on j.UserID= uj.UserID

multiple conditions in same column with relation

Here is the case:
There is a user table
id email orders_counter
=================================
1 a#a.com 5
2 b#b.com 3
3 c#c.com 0
And a user data table for user's other data
id user_id title value
=======================================
1 1 Name Peter Johnson
2 1 Tel 31546988
3 2 Name Alan Johnson
4 2 Tel 56984887
If I want to fetch all user that
1, orders_counter greater then 3
2, Name contain 'Johnson'
3, Tel contain '88'
AT THE SAME TIME
What is my sql, and if I want to do it rubyonrails way
what is the ActiveRecord code
I know I can it one by one and then join all of them together, but it waste too much resource while conditions build up
Select * From `user` u
inner join `userdata` d on d.user_id=u.id and d.title='Name' and d.value like '%Johnson%'
inner join `userdata` c on c.user_id=u.id and c.title='Tel' and c.value like '%88%'
where orders_counter > 3
the way that you've got your user data table structured, you'll almost always have to join on that table several times in order to "pivot" those values into columns. I'd recommend just creating a table that has name and tel as columns. then the query becomes a lot more simple
select * from `user` u
inner join `user_data` d on d.Tel like '%88%' and d.Name like '%johnson%'
where u.orders_counter > 3
try this one up,
SELECT a.*, b.*
FROM user a
INNER JOIN data b
ON a.id = b.user_ID
WHERE a.orders_counter > 3 AND
(b.title = 'NAME' AND b.value like '%johnson%') AND
(b.title = 'TEL' AND b.tel like '%88%')
try this:
select *
from user U join user_data D
on U.id=D.user_id
where U.orders_counter>3
and D.title='Name' and value like '%Johnson%'
and D.title='Tel' and value like '%88%'

SQL Join to only one record in one to many relationship

I have the following query:
SELECT c.[ClientID]
,u.[AccessId]
FROM [tblClient] c
INNER JOIN [tblUser] u ON u.[Id] = c.[UserId]
This tblUser has multiple ID's for each UserID row.
So it would look like this:
UserID AccessID
1 AD2F0A-965B78414-2B34906F2-0127AA5A
1 ID2F0A9-65B784-142B34906-F20127AA5A
1 UD2F0A9-65B78-4142B34906F-20127AA5A
2 TD2F0A9-65B784142-B34906F-20127AA5A
2 RD2F0A9-65B784142B3-4906-F20127AA5A
3 WD2F0A96-5B784142-B34906F201-27AA5A
3 ZD2F0A96-5B784-142B34-906F2-0127AA5A
3 CD2F0A965-B784142B3-4906F20-127AA5A
Is there a way to only get the top(or 1) AccessId for each UserID? It doesnt matter which AccessID i get, I just want 1 of them.
Thanks
SELECT c.[ClientID], MAX(u.[AccessId])
FROM [tblClient] c
INNER JOIN [tblUser] u
ON u.[Id] = c.[UserId]
GROUP BY c.[ClientID]

Problem creating a view containing lookup tables

should be simple enough but it's causing me a couple of issues.
I have a data set similar to the following:
User
UserID
Name
Age
UserPropertyValues
UserID
PropertyCodeValueID
PropertyCodes
PropertyCodeID
PropertyCodeName
PropertyCodeValues
PropertyCodeValueID
PropertyCodeID
PropertValue
Now let's assume the tables contain the following data:
1 John 25
2 Sarah 34
1 2
1 3
2 1
2 3
1 FavColour
2 CarMake
3 PhoneType
1 1 Blue
2 1 Yellow
3 2 Ford
4 3 Mobile
5 3 Landline
Now from this I'm looking to create a view to return the User details, as well as the property values for Property code 1 and 2 like so:
John 25 Yellow Ford
Sarah 34 Blue Ford
The queries I have tried so far tend to return repeating rows of data :
John 25 Yellow
John 25 Ford
Sarah 34 Blue
Sarah 34 Ford
Any help is appreciated, thank you all in advance.
Input data:
DECLARE #User TABLE (UserID INT, Name VARCHAR(10), Age INT)
INSERT INTO #User
SELECT 1, 'John', 25 UNION
SELECT 2, 'Sarah', 34
DECLARE #UserPropertyValues TABLE(UserID INT, PropertyCodeValueID INT)
INSERT INTO #UserPropertyValues
SELECT 1, 2 UNION
SELECT 1, 3 UNION
SELECT 2, 1 UNION
SELECT 2, 3
DECLARE #PropertyCodes
TABLE (PropertyCodeID INT, PropertyCodeName VARCHAR(10))
INSERT INTO #PropertyCodes
SELECT 1, 'FavColour' UNION
SELECT 2, 'CarMake' UNION
SELECT 3, 'PhoneType'
DECLARE #PropertyCodeValues TABLE (PropertyCodeValueID INT,
PropertyCodeID INT, PropertValue VARCHAR(10))
INSERT INTO #PropertyCodeValues
SELECT 1, 1, 'Blue' UNION
SELECT 2, 1, 'Yellow' UNION
SELECT 3, 2, 'Ford' UNION
SELECT 4, 3, 'Mobile' UNION
SELECT 5, 3, 'Landline'
If two properties is all that you need in result, and each user have those properties, then try this:
SELECT U.Name, U.Age, PCVFC.PropertValue, PCVCM.PropertValue
FROM #User U
INNER JOIN #UserPropertyValues UPVFC ON U.UserID = UPVFC.UserID
INNER JOIN #PropertyCodeValues PCVFC
ON UPVFC.PropertyCodeValueID = PCVFC.PropertyCodeValueID
AND PCVFC.PropertyCodeID = 1
INNER JOIN #UserPropertyValues UPVCM ON U.UserID = UPVCM.UserID
INNER JOIN #PropertyCodeValues PCVCM
ON UPVCM.PropertyCodeValueID = PCVCM.PropertyCodeValueID
AND PCVCM.PropertyCodeID = 2
[edit] But to handle possible NULL values better use this:
SELECT U.Name, U.Age, FC.PropertValue, CM.PropertValue
FROM #User U
LEFT JOIN (
SELECT UserID, PropertValue FROM #UserPropertyValues UPV
INNER JOIN #PropertyCodeValues PCV
ON UPV.PropertyCodeValueID = PCV.PropertyCodeValueID
AND PCV.PropertyCodeID = 1
) FC ON U.UserID = FC.UserID
LEFT JOIN (
SELECT UserID, PropertValue FROM #UserPropertyValues UPV
INNER JOIN #PropertyCodeValues PCV
ON UPV.PropertyCodeValueID = PCV.PropertyCodeValueID
AND PCV.PropertyCodeID = 2
) CM ON U.UserID = CM.UserID
What you really need to is abandon this type of database design as soon as humanly possible. It will never be either effective of efficient. To get three types of values you have to join to the table three times. Once you have 30 or forty differnt types of information, you will need to join to the table that many times (and left joins at that). Further everytime you want any information you will need to join to this table. I see this as creating a major locking issue in your database. The people who originally designed one of the databases I work with did this and caused a huge performance issue when the company grew from having one or two customers to the largest in our industry.
If the properties are ones that will likely only have one realted records per person, put them into the user table. If they will have multiple records then create a separate table for each type of information (one for hones, one for email, one for cartype, etc.) Since the information you will eventually want to collect will usually be more than the simple value and differnt for each type of information they must be in separate tables. Then when you only need to see one value (say phone number but not email) you join to just that table and you aren't interfeing with people trying to access email but not phone number. And if you have a yellow ford or white Honda, it will be stored in only one record in the auto table rather than two property records in your design.
SELECT
u.[Name],
u.Age,
pcv1.PropertValue,
pcv2.PropertValue
FROM
Users u
LEFT JOIN
( UserPropertyValues upv1
JOIN PropertyCodeValues pcv1 ON
upv1.PropertyCodeValueID = pcv1.PropertyCodeValueID
AND pcv1.PropertyCodeID = 1
)
ON upv1.UserID = u.UserID
LEFT JOIN (
UserPropertyValues upv2
JOIN PropertyCodeValues pcv2 ON
upv2.PropertyCodeValueID = pcv2.PropertyCodeValueID
AND pcv2.PropertyCodeID = 2
)
ON upv2.UserID = u.UserID
Edit : I renamed user to users
Edit2 : Allow for null (not entered values)