How to search for a list of combinations of two columns in a table? - sql

I have a list of combinations from a table (First_Name and Start month of employment).
I need to be able to search the table and determine what entries in the table are not in the list. Ex. (Mark, January exists but Mark, March doesn't.)
Is there a shorter way to do this than append every combination to the SQL? This is a pretty long list out of a pretty large table.

You can CROSS JOIN employee names with list of months to create all possible combinations. LEFT JOIN for the rest:
SELECT
FROM (
SELECT DISTINCT First_Name FROM t
) AS emp
CROSS JOIN (VALUES
('January'), ('February'), ('March'), ('April'), ('May'), ('June'),
('July'), ('August'), ('September'), ('October'), ('November'), ('December')
) mon(Month_Name)
LEFT JOIN t ON emp.First_Name = t.First_Name AND mon.Month_Name = t.Month_Name
WHERE t.PK IS NULL

Use joins to a temporal table or equivalent expressions. Something like
SELECT t.*
FROM ( SELECT 'Mike' AS name, 'March' AS month
UNION SELECT 'John' AS name, 'April' AS month) AS aux
JOIN table t ON t.name = aux.name
AND t.month = aux.name
Of course it can be a literal temporary or standard table if you alredy have the data.

You need a left join or not exists for this. For instance:
select v.*
from (values ('Mark', 'January'), ('Mark', 'March')) v(name, mon)
where not exists (select 1
from t
where t.name = v.name and t.mon = v.mon
);

Related

Display the names of each employees who works in both ‘IT’ and ‘SE’

Emp(sid(pk) : integer, sname: varchar(255))
Dep(sid(fk) : integer, dep : varchar(255))
SQL:How I find the names of each employees who works in both ‘IT’ and ‘SE’?
To observe a query that Joins two tables together and get common values depend on a common column Ex: id, using INNTER JOIN will help you on that
The INNER JOIN keyword selects records that have matching values in both tables.
Solution
SELECT Emp.sid, Emp.sname FROM Emp
INNER JOIN
(SELECT sid FROM Dep WHERE dep='IT'
INTERSECT
SELECT sid FROM Dep WHERE dep='SE') as A
ON Emp.sid = A.sid
References
SQL INNER JOIN
The way I understand it, the data situation is as below. emp with sid and sname, and dep, with sid - the foreign key to emp and dep, this time not as a table, but a column containing the department's abbreviation. And the combination, in the dep table, of sid and dep, is unique.
If that is the constellation, then join the two tables using sid, filter by: dep in the set:('IT' , 'SE'); Then, put the two columns from emp into the the column list, and GROUP BY them, and finally, apply the grouping filter HAVING COUNT(*) = 2 to just get the group that has two entries when filtered by the two departments.
WITH
emp(sid, sname) AS (
SELECT 42,'Arthur'
UNION ALL SELECT 43,'Ford'
UNION ALL SELECT 44,'Zaphod'
)
,
dep(sid, dep) AS (
SELECT 42,'IT'
UNION ALL SELECT 42,'SE'
UNION ALL SELECT 42,'AC'
UNION ALL SELECT 43,'IT'
UNION ALL SELECT 43,'AC'
UNION ALL SELECT 44,'SE'
UNION ALL SELECT 44,'SA'
)
SELECT
emp.sid
, emp.sname
FROM emp JOIN dep USING(sid)
WHERE dep.dep IN ('IT','SE')
GROUP BY
emp.sid
, emp.sname
HAVING COUNT(*) = 2;
-- out sid|sname
-- out 42|Arthur

Determine if where in clause in select query did not return a value

I have a SQL statement which is searching the database for a few records.
SELECT UserName
, Location
FROM People_DB
WHERE UserName IN ('Nick', 'Jack', 'Robert')
The first two names ('Nick', 'Jack') exist in the People_DB and my query returns that information however it does not return 'Robert' since it does not exist in my database.
Is there a way to return 'Robert is not in People_DB' in the query result set?
there is no straight way of doing this.
You can use something like this
drop table #temp
select 'Nick' as UserName into #temp
union select 'Jack'
union select 'Robert'
SELECT t.UserName,isnull(b.UserName,'Doesn''t exists',b.Location
FROM #temp t
left join People_DB b on t.UserName = b.UserName
WHERE b.UserName IN ('Nick', 'Jack', 'Robert')
You need to construct a "table" with the list of names. That is the only way to get them back in the result set.
If your databases supports the values() constructor, I would recommend:
SELECT u.UserName, p.Location
FROM (VALUES ('Nick'), ('Jack'), ('Robert') ) v(UserName) LEFT JOIN
People_DB p
ON p.UserName = v.UserName;
I should note that alternative solutions are available in almost any database that doesn't support VALUES. For instance:
SELECT u.UserName, p.Location
FROM (SELECT 'Nick' as UserName UNION ALL
SELECT 'Jack' UNION ALL
SELECT 'Robert'
) v LEFT JOIN
People_DB p
ON p.UserName = v.UserName;
A NULL location is probably sufficient indication that the name is not found. If you want an actual flag, you can use CASE:
SELECT u.UserName, p.Location,
(CASE WHEN p.UserName IS NULL THEN 0 ELSE 1 END) as in_list_flag
FROM (VALUES ('Nick'), ('Jack'), ('Robert') v(UserName) LEFT JOIN
People_DB p
ON p.UserName = v.UserName;

How the following SQL query is working?

How is the given query correct as it is using T1 inside the with clause and T1 is declared after the clause within WITH is completed.
WITH T1(Emp,Manager,Salary) AS
(
SELECT tt2.[Emp],tt2.[Manager],tt2.[Salary]
FROM [YourTable] AS tt1
RIGHT OUTER JOIN [YourTable] AS tt2 ON tt1.[Emp]=tt2.[Manager]
WHERE tt1.[Emp] is NULL
UNION ALL
SELECT r.[Emp],T1.[Manager],r.[Salary]
FROM [YourTable] AS r
INNER JOIN T1 ON r.[Manager]=T1.[Emp]
)
SELECT [Manager],SUM([Salary]) AS Salary
FROM T1
GROUP BY [Manager]
ORDER BY SUM([Salary]) DESC
Above query is answer to following question -
I have table with columns (Employee, Manager, Salary). Need to calculate aggregate salary for all employees corresponding to top-level managers in one SQL. For example
Input table is :
Emp Manager Salary
A T 10
B A 11
C F 13
D B 5
Result should be :
Top-Lvl Manager Salary(agg)
T 26
F 13
Manager-Employee layering can go multiple levels.
This is a recursive query. The part before UNION ALL gets the base records. The part after it recursively gets more rows attatched to the former.
The first part is confusingly written. It is an anti-join pattern inplemented even with a right outer join which many consider hard to read. It merely means this:
select emp, manager, salary
from yourtable
where manager not in (select emp from yourtable);
So you get all employees that have no manager (i.e. the super managers).
With the part after UNION ALL you get their subordinates and the subordinates of these etc. A hierarchical query.
At last in
SELECT [Manager],SUM([Salary]) AS Salary
FROM T1
GROUP BY [Manager]
ORDER BY SUM([Salary]) DESC
you use those rows to get a cumulated salary per manager.
You can read up on recursive queries in SQL Server here: https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx.
EDIT - Less over-kill
Declare #YourTable table (Emp varchar(25),Manager varchar(25),Salary int)
Insert into #YourTable values
('A','T',10),
('B','A',11),
('C','F',13),
('D','B',5)
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by Emp) as varchar(500))
,Emp=Manager
,Manager=cast(null as varchar(25))
,Lvl=1
,Salary = 0
From #YourTable
Where Manager Not In (Select Distinct Emp From #YourTable)
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.Emp)) as varchar(500))
,r.Emp
,r.Manager
,p.Lvl+1
,r.Salary
From #YourTable r
Join cteP p on r.Manager = p.Emp)
Select TopLvl = A.Emp
,Salary = sum(B.Salary)
from cteP A
Join cteP B on (B.Seq Like A.Seq+'%')
Where A.Lvl=1
Group By A.Emp
Returns
TopLvl Salary
F 13
T 26
Inside with T1 references to INNER JOIN T1 ON r.[Manager]=T1.[Emp] which is probably table in your DB. Outside with T1 references to result of with statement.

Recursive query in SQL Server

I have a table with following structure
Table name: matches
That basically stores which product is matching which product. I need to process this table
And store in a groups table like below.
Table Name: groups
group_ID stores the MIN Product_ID of the Product_IDS that form a group. To give an example let's say
If A is matching B and B is Matching C then three rows should go to group table in format (A, A), (A, B), (A, C)
I have tried looking into co-related subqueries and CTE, but not getting this to implement.
I need to do this all in SQL.
Thanks for the help .
Try this:
;WITH CTE
AS
(
SELECT DISTINCT
M1.Product_ID Group_ID,
M1.Product_ID
FROM matches M1
LEFT JOIN matches M2
ON M1.Product_Id = M2.matching_Product_Id
WHERE M2.matching_Product_Id IS NULL
UNION ALL
SELECT
C.Group_ID,
M.matching_Product_Id
FROM CTE C
JOIN matches M
ON C.Product_ID = M.Product_ID
)
SELECT * FROM CTE ORDER BY Group_ID
You can use OPTION(MAXRECURSION n) to control recursion depth.
SQL FIDDLE DEMO
Something like this (not tested)
with match_groups as (
select product_id,
matching_product_id,
product_id as group_id
from matches
where product_id not in (select matching_product_id from matches)
union all
select m.product_id, m.matching_product_id, p.group_id
from matches m
join match_groups p on m.product_id = p.matching_product_id
)
select group_id, product_id
from match_groups
order by group_id;
Sample of the Recursive Level:
DECLARE #VALUE_CODE AS VARCHAR(5);
--SET #VALUE_CODE = 'A' -- Specify a level
WITH ViewValue AS
(
SELECT ValueCode
, ValueDesc
, PrecedingValueCode
FROM ValuesTable
WHERE PrecedingValueCode IS NULL
UNION ALL
SELECT A.ValueCode
, A.ValueDesc
, A.PrecedingValueCode
FROM ValuesTable A
INNER JOIN ViewValue V ON
V.ValueCode = A.PrecedingValueCode
)
SELECT ValueCode, ValueDesc, PrecedingValueCode
FROM ViewValue
--WHERE PrecedingValueCode = #VALUE_CODE -- Specific level
--WHERE PrecedingValueCode IS NULL -- Root

sql - find max value

I have a table with 3 columns: Code, Year, percentage.
I need to return the code with the lowest (minimal) percentage in 2009. After this, I want also the name of the code, that exsist in other table that I made.
I only think using CREATE VIEW, but I prefer not to do so.
Select table.code, table.year, table.percentage, othertable.name
from table
inner join othertable on table.FKId = othertable.PKid
where year = 2009
and percentage =
(select min(percentage)
from table
where year = 2009)
Updated to include the othertable... since we don't have names.
UPDATED
Now that we have table names... Updated 3rd time now that I know year is string.
Select E.Code, C.Name
From dbo.Exam E
inner join dbo.Course C
ON E.Code = C.Code
Where E.Year = '2009' and --<-- PROBLEM LIKELY HERE year was string not int.
E.Fail = (select MIN(E2.Fail)
from dbo.Exam E2 where E2.Year = '2009') --<--Don't forget here too.
From comments: sample Data:
INSERT INTO Exam VALUES(333,'2009',40)
INSERT INTO Exam VALUES(333,'2009',20)
INSERT INTO Exam VALUES(555,'2009',19)
INSERT INTO Exam VALUES(444,'2009',19)
INSERT INTO Exam VALUES(777,'2009',23)
INSERT INTO Exam VALUES(333,'2009',0)
INSERT INTO Course VALUES(111,'Name1',5)
INSERT INTO Course VALUES(333,'Name2',5)
INSERT INTO Course VALUES(444,'Name3',6)
INSERT INTO Course VALUES(555,'Name4',3)
INSERT INTO Course VALUES(777,'Name5',3)
INSERT INTO Course VALUES(999,'Name6',6)
Assumption is result should be
Name2, 5
You can use a group by to find the lowest percentage for a year, and then join back to the main tables to find the corresponding other columns:
select *
from CodeYearPercTbl cyp
join CodeTbl c
on c.Code = cyp.Code
join (
select Year
, min(Percentage) as MinPerc
from CodeYearPercTbl
group by
Year
) as filter
on filter.Year = cyp.Year
and filter.MinPerc = cyp.Percentage
where cyp.Year = 2009
Table-1 : Code,Year,Percentage
Table-2 : Code,CodeName
select T1.Code,T2.CodeName,T1.Percentage from
(
select TOP 1 Code,Percentage
from Table-1
where Year = '2009'
order by Percentage asc
) T1 inner join Table-2 T2 on T1.Code = T2.Code
Try this:
select a.Code, c.Name
from YourTable a inner join AnotherTable c on a.Code = c.Code
where a.Percentage = (select MIN(Percentage)
from YourTable b where b.Year = '2009'
)