Find missing setting - sql-server-2012

I have two tables in my DB. Table A is informational data table and Table B is a setting table. How do I find Table A is missing one of the setting in Table B.
E.G.
Table A
username setting
Mark 1
Mark 2
Martin 2
Jane 1
Table B
Possible_Setting
1
2
3
Result Table
username missing_setting
Mark 3
Martin 1
Martin 3
Jane 2
Jane 3
Thanks for help!

This may be inefficient if table sizes are significant, owing to the cross join but its the only answer I could come up with.
SELECT a.username, b.Possible_Setting AS missing_setting
FROM
(SELECT DISTINCT username FROM TableA a) a
CROSS JOIN TableB b
WHERE
NOT EXISTS (
SELECT *
FROM TableA real_a
WHERE real_a.username = a.username
AND real_a.setting = b.Possible_Setting)
ORDER BY 1, 2
Setup code:
CREATE TABLE TableA (username varchar(20), setting tinyint)
CREATE TABLE TableB (Possible_Setting tinyint PRIMARY KEY)
INSERT TableA VALUES
('Mark', 1),
('Mark', 2),
('Martin', 2),
('Jane', 1)
INSERT TableB VALUES
(1),
(2),
(3)

Related

Get every unique pair combination of a column in SQL

Lets say I have given table:
1 A
2 A
3 A
How do I JOIN / combine the table with itself so I get every possible unique pair combination of the first column:
1 1 A
1 2 A
1 3 A
2 1 A
2 2 A
2 3 A
...
You can do something like this.
Cross JOIN is used for cross product
-- create
CREATE TABLE EMPLOYEE (
empId INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
-- insert
INSERT INTO EMPLOYEE VALUES (0001, 'Clark');
INSERT INTO EMPLOYEE VALUES (0002, 'Dave');
INSERT INTO EMPLOYEE VALUES (0003, 'Ava');
-- fetch
SELECT e1.empId, e2.empId, e1.name FROM EMPLOYEE e1
CROSS JOIN EMPLOYEE e2;

SQL Select Where Opposite Match Does Not Exist

Trying to compare between two columns and check if there are no records that exist with the reversal between those two columns. Other Words looking for instances where 1-> 3 exists but 3->1 does not exist. If 1->2 and 2->1 exists we will still consider 1 to be part of the results.
Table = Betweens
start_id | end_id
1 | 2
2 | 1
1 | 3
1 would be added since it is a start to an end with no opposite present of 3,1. Though it did not get added until the 3rd entry since 1 and 2 had an opposite.
So, eventually it will just return names where the reversal does not exist.
I then want to join another table where the number from the previous problem has its name installed on it.
Table = Names
id | name
1 | Mars
2 | Earth
3 | Jupiter
So results will just be the names of those that don't have an opposite.
You can use a not exists condition:
select t1.start_id, t1.end_id
from the_table t1
where not exists (select *
from the_table t2
where t2.end_id = t1.start_id
and t2.start_id = t1.end_id);
I'm not sure about your data volume, so with your ask, below query will supply desired result for you in Sql Server.
create table TableBetweens
(start_id INT,
end_id INT
)
INSERT INTO TableBetweens VALUES(1,2)
INSERT INTO TableBetweens VALUES(2,1)
INSERT INTO TableBetweens VALUES(1,3)
create table TableNames
(id INT,
NAME VARCHAR(50)
)
INSERT INTO TableNames VALUES(1,'Mars')
INSERT INTO TableNames VALUES(2,'Earth')
INSERT INTO TableNames VALUES(3,'Jupiter')
SELECT *
FROM TableNames c
WHERE c.id IN (
SELECT nameid1.nameid
FROM (SELECT a.start_id, a.end_id
FROM TableBetweens a
LEFT JOIN TableBetweens b
ON CONCAT(a.start_id,a.end_id) = CONCAT(b.end_id,b.start_id)
WHERE b.end_id IS NULL
AND b.start_id IS NULL) filterData
UNPIVOT
(
nameid
FOR id IN (filterData.start_id,filterData.end_id)
) AS nameid1
)

Where condition based on link table

I have a table of Users. Each User can be in multiple Disciplines, and they are linked by a link table, User_Discipline. The tables are pretty straight forward:
User
ID Name more...
3 | John Doe | ...
7 | Jane Smith | ...
12 | Joe Jones | ...
Discipline
ID name
1 | Civil
2 | Mechanical
3 | Piping
User_Discipline
UserID DisciplineID
3 | 2
3 | 1
7 | 2
12 | 3
Say John Doe is the logged in user. He needs to be able to select a list of all of the users in any of his disciplines. In the given example, I need a query that would return a list with John and Jane, since John is both Civil and Mechanical, and Jane is in Mechanical.
I think sub-selects are involved, but all the reading I've done so far have shown how to do subselects checking for one value (say, John's Civil Discipline). I need to be able to perform a query that runs a WHERE condition but matches any of John's Disciplines many-to-many with others' Disciplines.
I'm using the DataTables Editor .NET library to do the SQL, but I can translate an answer in regular SQL markup to that library. The only limitation of the library that I might encounter here is that everything would have to be done in one SQL statement. I appreciate any help!
Something like this?
SELECT DISTINCT [User].ID, [User].Name
FROM [User]
JOIN User_Discipline
ON [User].ID = User_Discipline.UserID
WHERE
User_Discipline.DisciplineID IN (
SELECT DisciplineID
FROM User_Discipline
WHERE UserID = <<John Doe's userID>>
)
You can do it all with inner joins:
declare #users table (id int, fullname varchar(50))
declare #disciplines table (id int, discname varchar(50))
declare #userdisciplines table (userid int, discid int)
insert into #users VALUES (3, 'John Doe')
insert into #users VALUES (7, 'Jane Smith')
insert into #users VALUES (12, 'Joe Jones')
insert into #disciplines VALUES (1, 'Civil')
insert into #disciplines VALUES (2, 'Mechanical')
insert into #disciplines VALUES (2, 'Piping')
insert into #userdisciplines VALUES (3, 2)
insert into #userdisciplines VALUES (3, 1)
insert into #userdisciplines VALUES (7, 2)
insert into #userdisciplines VALUES (12, 3)
SELECT distinct id, fullname from #users u
INNER JOIN #userdisciplines ud ON ud.userid = u.id
INNER JOIN
(SELECT ud.discid FROM #users u
inner join #userdisciplines ud on ud.userid = u.id
WHERE u.fullname = 'John Doe') d ON d.discid = ud.discid

Check duplicates in sql table and replace the duplicates ID in another table

I have a table with duplicate entries (I forgot to make NAME column unique)
So I now have this Duplicate entry table called 'table 1'
ID NAME
1 John F Smith
2 Sam G Davies
3 Tom W Mack
4 Bob W E Jone
5 Tom W Mack
IE ID 3 and 5 are duplicates
Table 2
ID NAMEID ORDERS
1 2 item4
2 1 item5
3 4 item6
4 3 item23
5 5 item34
NAMEID are ID from table 1. Table 2 ID 4 and 5 I want to have NAMEID of 3 (Tom W Mack's Orders) like so
Table 2 (correct version)
ID NAMEID ORDERS
1 2 item4
2 1 item5
3 4 item6
4 3 item23
5 3 item34
Is there an easy way to find and update the duplicates NAMEID in table 2 then remove the duplicates from table 1
In this case what you can do is.
You can find how many duplicate records you have.
In Order to find duplicate records you can use.
SELECT ID, NAME,COUNT(1) as CNT FROM TABLE1 GROUP BY ID, NAME
This is will give you the count and you find all the duplicate records
and delete them manually.
Don't forget to alter your table after removing all the duplicate records.
Here's how you can do it:
-- set up the environment
create table #t (ID int, NAME varchar(50))
insert #t values
(1, 'John F Smith'),
(2, 'Sam G Davies'),
(3, 'Tom W Mack'),
(4, 'Bob W E Jone'),
(5, 'Tom W Mack')
create table #t2 (ID int, NAMEID int, ORDERS varchar(10))
insert #t2 values
(1, 2, 'item4'),
(2, 1, 'item5'),
(3, 4, 'item6'),
(4, 3, 'item23'),
(5, 5, 'item34')
go
-- update the referencing table first
;with x as (
select id,
first_value(id) over(partition by name order by id) replace_with
from #t
),
y as (
select #t2.nameid, x.replace_with
FROM #t2
join x on #t2.nameid = x.id
where #t2.nameid <> x.replace_with
)
update y set nameid = replace_with
-- delete duplicates from referenced table
;with x as (
select *, row_number() over(partition by name order by id) rn
from #t
)
delete x where rn > 1
select * from #t
select * from #t2
Pls, test first for performance and validity.
Let's use the example data
INSERT INTO TableA
(`ID`, `NAME`)
VALUES
(1, 'NameA'),
(2, 'NameB'),
(3, 'NameA'),
(4, 'NameC'),
(5, 'NameB'),
(6, 'NameD')
and
INSERT INTO TableB
(`ID`, `NAMEID`, `ORDERS`)
VALUES
(1, 2, 'itemB1'),
(2, 1, 'itemA1'),
(3, 4, 'itemC1'),
(4, 3, 'itemA2'),
(5, 5, 'itemB2'),
(5, 6, 'itemD1')
(makes it a bit easier to spot the duplicates and check the result)
Let's start with a simple query to get the smallest ID for a given NAME
SELECT
NAME, min(ID)
FROM
tableA
GROUP BY
NAME
And the result is [NameA,1], [NameB,2], [NameC,4], [NameD,6]
Now if you use that as an uncorrelated subquery for a JOIN with the base table like
SELECT
keep.kid, dup.id
FROM
tableA as dup
JOIN
(
SELECT
NAME, min(ID) as kid
FROM
tableA
GROUP BY
NAME
) as keep
ON
keep.NAME=dup.NAME
AND keep.kid<dup.id
It finds all duplicates that have the same name as in the result of the subquery but a different id + it also gives you the id of the "original", i.e. the smallest id for that name.
For the example it's [1,3], [2,5]
Now you can use that in an UPDATE query like
UPDATE
TableB as b
JOIN
tableA as dup
JOIN
(
SELECT
NAME, min(ID) as kid
FROM
tableA
GROUP BY
NAME
) as keep
ON
keep.NAME=dup.NAME
AND keep.kid<dup.id
SET
b.NAMEID=keep.kid
WHERE
b.NAMEID=dup.id
And the result is
ID,NAMEID,ORDERS
1, 2, itemB1
2, 1, itemA1
3, 4, itemC1
4, 1, itemA2 <- now has NAMEID=1
5, 2, itemB2 <- now has NAMEID=2
5, 6, itemD1
To eleminate the duplicates from tableA you can use the first query again.

Sql to update login id's dynamically based on count

I have 2 tables. One is main table and other one is login table. I may have 10 Records in Main table and 6 Records in login table. Each login id has to be assingned equally to main table. Can any one please give me the best solution to update the login information.
Example
Create table ##t1
(id int identity,
name varchar(5),
loginid varchar(10)
divno char(3))
create table ##l1
(
id int identity,
name varchar(10),divno char(3))
insert into ##t1 values
('Jin',null,'001')
insert into ##t1 values
('Anu',null,'001')
insert into ##t1 values
('kir',null'002')
insert into ##t1 values
('Asi',null,'003')
insert into ##t1 values
('Nil',null,'002')
insert into ##t1 values
('sup',null,'003')
insert into ##t1 values
('amu',null,'003')
insert into ##t1 values
('mani',null,'003')
insert into ##l1 values
('A','001')
insert into ##l1 values
('B','001')
insert into ##l1 values
('C','002')
insert into ##l1 values
('D','002')
insert into ##l1 values
('E','002')
insert into ##l1 values
('F','003')
Data Example
Main table
id name loginid divno
----------- ----- ----------
1 Jin NULL 001
2 Anu NULL 001
3 kir NULL 002
4 Asi NULL 003
5 Nil NULL 002
6 sup NULL 003
7 amu NULL 003
8 mani NULL 003
Login Table
id name divno
----------- -------------
1 A 001
2 B 001
3 C 002
4 D 002
5 E 002
6 F 003
desired output
How can we do this without looping?
update ##t1
set loginid = #l1.name
from
##t1
inner join
(select *, (ROW_NUMBER() Over (order by id) -1)% (select COUNT(*) from ##l1)+1 as rn from ##t1) v
on ##t1.id = v.id
inner join
##l1
on v.rn = ##l1.id
Let me do this as a select query rather than as an upadte.
select id, name, l.login
from (select mt.*,
(row_number() over (order by id) % l.loginCount) + 1 as loginSeqnum
from MainTable mt cross join
(select count(*) as loginCount from login) l
) mt join
(select l.*, row_number() over (order by id) as seqnum
from login l
) l
on mt.LoginSeqnum = l.seqnum
What this is doing is adding a sequence number to the logins (just in case loginid is not 1..n. It then calculates a similar value for each record in the first table.
One nice thing about this method is you can modify it to get more random orderings, by changing the "order by" clause in the row_number() statements. For instance, using "order by newid()" will randomize the assignment, rather than doing it in a round-robin fashion.