sql server 2008 merge - sql-server

Considering the following tables:
Users
----------------------
Id | 1 | 2
Name| John| Jack
Cars
----------------------
Id | 14 | 26
Name| Mercedes| BMW
Import
-----------------------
Id | 12 | 34
UserName | John | Daniel
UserId | NULL | NULL
CarName | BMW | Mercedes
CarId | NULL | NULL
SomeOtherId| 45 | 45
I basically want to find UserId and CarId in the other tables, If they are not find they remain NULL,
I tried something like
UPDATE Import i
SET UserId = ( SELECT Id FROM Users WHERE Name=i.UserName ),
CarId = ( SELECT Id FROM Cars WHERE Name= i.CarName )
WHERE SomeOtherId=45;
In this case i am getting an incorrect syntax.
How can i fix it?
Is it a way to use MERGE in this case? (merging more than 2 tables and using a WHERE clause)?
Thanks.

You might use from clause in update itself:
UPDATE i
SET UserId = Users.Id,
CarId = Cars.Id
FROM Import i
LEFT JOIN Users
ON i.UserName = Users.Name
LEFT JOIN Cars
ON i.CarName = Cars.Name
WHERE i.SomeOtherId=45;

Hope the following will solve your problem
UPDATE I
SET UserId = U.Id
FROM IMPORT I
INNER JOIN Users U
ON I.UserName = U.Name
WHERE UserId is Null
UPDATE I
SET CarId = C.Id
FROM IMPORT I
INNER JOIN Cars C
ON I.CarName = C.Name
WHERE CarId is Null

Related

SQL Server: join one table with two table have same names but select all data from table first if available, otherwise from second table

I tried searching this kind of use case on google but did found exact what i was looking so, Need help to reach to specific point by useful answer, docs or any reference
I have this situation: table A has to be joined with table B and table C; B and C have similar columns, so there will be duplicate column names in select part, however give preferences to all the data of tables B if available otherwise show data from table c
For example:
SELECT
ae.*, ml.name as name, ml.contact AS contact,
ve.name AS name, ve.contact AS contact
FROM
TABLE ae
LEFT JOIN
TABLE ml ON ae.eid = ml.eid
LEFT JOIN
TABLE ve ON ae.eid = ve.eid
WHERE
ae.eid = 1
ml data
eid | name | contact
----+------+--------
1 | xyz | null
ve data
eid | name | contact
----+------+--------
1 | xyz | 1
ae data
eid | gender
----+--------
1 | male
I want this result:
eid | gender | name | contact
----+--------+------+--------
1 | male | xyz | null
But I am getting this for now:
eid | gender | name | contact | contact
----+--------+------+---------+--------
1 | male | xyz | 1 | null
I'm using node-mssql driver for querying SQL Server data.
Thanks,
You must join ve only if there is no matching row in ml and you do it if you add in the ON clause the condition ... AND ml.eid IS NULL.
Also use COALESCE() to select the columns from ml first and if they don't exist from ve:
SELECT ae.*,
COALESCE(ml.name, ve.name) AS name,
COALESCE(ml.contact, ve.contact) AS contact
FROM ae
LEFT JOIN ml ON ae.eid = ml.eid
LEFT JOIN ve ON ae.eid = ve.eid AND ml.eid IS NULL
WHERE ae.eid = 1
See the demo.
Results:
eid
gender
name
contact
1
male
xyz
null

In PostgreSQL: how to transform an array field containing FKs to array of names from referenced table?

My database has a table of Products, like so:
PRODUCTS
----------------------------
id | name | suppliers
----------------------------
1 | widget | {1,2}
2 | gizmo | {1}
3 | geegaw | {3}
4 | tchotchke | null
The suppliers column contains an array (numeric[]) of IDs belonging to a table of suppliers:
SUPPLIERS
------------
id | name
------------
1 | alpha
2 | beta
3 | gamma
How can I write a query that will return the contents of PRODUCTS except with an array of supplier names instead of supplier ID numbers? Result should look like this:
-----------------------------------
id | name | suppliers
-----------------------------------
1 | widget | {'alpha','beta'}
2 | gizmo | {'alpha'}
3 | geegaw | {'gamma'}
4 | tchotchke | null
Succinct and efficient methods would be preferred, but readability/understandability are also nice.
Edit: This isn't a duplicate of the linked question, although that question does involve the unnest operation, it doesn't re-aggregate the result. The answer to this question makes a new contribution to the site.
select t1.id, t1.name, array_agg(s.Name) as Suppliers
from Products t1
left join lateral (
select unnest(suppliers) as supplierId from myProducts t2
where t1.id = t2.id) as t on true
left join Suppliers s on s.Id = t.supplierID
group by t1.id, t1.name;
What was I thinking. Here is a better version:
select p.id, p.name, array_agg(s.Name) as Suppliers
from (select *,unnest(suppliers) as supplierId from Products) p
left join Suppliers s on s.Id = p.supplierID
group by p.id, p.name;

SQL Server - How to check if two items have the same relations to another set of items?

I have table with Employees (tblEmployee):
| ID | Name |
| 1 | Smith |
| 2 | Black |
| 3 | Thompson |
And a table with Roles (tblRoles):
| ID | Name |
| 1 | Submitter |
| 2 | Receiver |
| 3 | Analyzer |
I have also a table with relations of Employees to their Roles with many to many relation type (tblEmployeeRoleRel):
| EmployeeID | RoleID |
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 3 | 3 |
I need to select ID, Name from tblEmployee that have exaclty the same set of roles from tblEmployeeRoleRel as has the Employee with ID = 1. How can I do it?
Use a where clause to limit the roles you're looking at to those of employeeID of 1 and use a having clause to make sure that the employee's role count matches that of employee1.
SELECT A.EmployeeID
FROM tblEmployeeRoleRel A
WHERE Exists (SELECT 1
FROM tblEmployeeRoleRel B
WHERE B.EmployeeID = 1
and B.RoleID = A.RoleID)
GROUP BY A.EmployeeID
HAVING count(A.RoleID) = (SELECT count(C.RoleID)
FROM tblEmployeeRoleRel C
WHERE EmployeeID = 1)
This assumes that employeeID and roleID are unique in tblEmployeeRoleRel otherwise we may have to distinct the roleID fields above.
Declare #EmployeeID int = 1 -- change this to whatever employee ID you like, or perhaps you'd pass an Employee ID to it in a stored procedure.
Select Distinct e.EmployeeID -- normally distinct would incur extra overhead, but in this case you only want the employee IDs. not using Distinct when an employee has multiple roles will give you multiple employee IDs.
from tblEmployeeRoleRel as E
where E.EmployeeID not in
(Select EmployeeID from tblEmployeeRoleRel where RoleID not in (Select RoleID from tblEmployeeRoleRel where Employee_ID = #EmployeeID))
and exists (Select EmployeeID from tblEmployeeRoleRel where EmployeeID = e.EmployeeID) -- removes any "null" matches.
and E.Employee_ID <> #Employee_ID -- this keeps the employee ID itself from matching.

SQL Query Error

Please check the SQL schema and query on SQL Fiddle
I'm getting repetitive records with NULL values, if anyone can rectify the problem.
Regards
This is what I am getting:
| MEM_ID | MEM_EMAIL | GENDER | EDUCATION | PROFESSION |
|--------|----------------|--------|-----------|-------------|
| 1 | it#email.com | Male | (null) | (null) |
| 1 | it#email.com | (null) | Graduate | (null) |
| 1 | it#email.com | (null) | (null) | Engineer |
| 2 | info#email.com | Female | (null) | (null) |
| 2 | info#email.com | (null) | Graduate | (null) |
| 2 | info#email.com | (null) | (null) | Not Working |
but I need
| MEM_ID | MEM_EMAIL | GENDER | EDUCATION | PROFESSION |
|--------|----------------|--------|-----------|-------------|
| 1 | it#email.com | Male | Graduate | Engineer |
| 2 | info#email.com | Female | Graduate | Not Working |
|
Ah yes, the famous Inner-Platform effect, where you try to implement relations by creating "attribute-value" tables and assigning magic strings for data types and values, then try to retrieve values with massive self-joins at runtime.
Only madness lies down this road. SQL already includes features for enforcing key values and referential integrity; don't try to implement this yourself. It's especially frustrating because your schema is actually quite simple:
CREATE TABLE [dbo].Member(
ID INT PRIMARY KEY,
Email Varchar(50) NOT NULL,
GenderID INT NOT NULL,
EducationID INT,
ProfessionID INT
)
CREATE TABLE [dbo].Gender(
GenderID INT PRIMARY KEY,
GenderName Varchar(50) NOT NULL
)
CREATE TABLE [dbo].Education(
EducationID INT PRIMARY KEY,
EducationName Varchar(50) NOT NULL
)
CREATE TABLE [dbo].Profession(
ProfessionID INT PRIMARY KEY,
ProfessionName Varchar(50) NOT NULL
)
Assign your magic values to Gender, Education, and Profession rows and assign their IDs to Member. You can perform full lookups with a simple:
SELECT ID, Email, GenderName, EducationName, ProfessionName
FROM Member m
JOIN Gender g ON g.GenderID=m.GenderID
LEFT JOIN Education e ON e.EducationID=m.EducationID
LEFT JOIN Profession p ON p.ProfessionID=m.ProfessionID
WHERE ...
You want to enforce values? Make the Member columns NOT NULL. Want to allow, say, only a single instance of each Education row per member? Foreign-key constraints already support this, no need to invent your own query language.
I think you are basically trying to do a pivot on your data. This is one way to accomplish that.
SELECT M.mem_Id,
M.mem_email,
[Gender] = (select max( A.att_value)
from tbl_attributes A
inner join tbl_mem_att_values MAV
on MAV.att_id = A.att_id
inner join tbl_types T
on T.type_id = A.type_id
where T.type_name = 'Gender'
and MAV.mem_Id = M.mem_Id),
[Education] = (select max( A.att_value)
from tbl_attributes A
inner join tbl_mem_att_values MAV
on MAV.att_id = A.att_id
inner join tbl_types T
on T.type_id = A.type_id
where T.type_name = 'Education'
and MAV.mem_Id = M.mem_Id),
[Profession] = (select max( A.att_value)
from tbl_attributes A
inner join tbl_mem_att_values MAV
on MAV.att_id = A.att_id
inner join tbl_types T
on T.type_id = A.type_id
where T.type_name = 'Profession'
and MAV.mem_Id = M.mem_Id)
FROM tbl_members M
The result looks like this
EM_ID MEM_EMAIL GENDER EDUCATION PROFESSION
1 it#email.com Male Graduate Engineer
2 info#email.com Female Graduate Not Working
Here's what you need...
SQL Fiddle
SELECT M.mem_Id,
M.mem_email,
( SELECT AA.att_value
FROM tbl_mem_att_values mv
JOIN tbl_attributes AA ON AA.att_id = mv.att_id
JOIN tbl_types TG ON TG.type_name = 'Gender' AND TG.type_id = aa.type_id
WHERE mv.mem_id = M.mem_Id) AS Gender,
( SELECT AA.att_value
FROM tbl_mem_att_values mv
JOIN tbl_attributes AA ON AA.att_id = mv.att_id
JOIN tbl_types TG ON TG.type_name = 'Education' AND TG.type_id = aa.type_id
WHERE mv.mem_id = M.mem_Id) AS Education,
( SELECT AA.att_value
FROM tbl_mem_att_values mv
JOIN tbl_attributes AA ON AA.att_id = mv.att_id
JOIN tbl_types TG ON TG.type_name = 'Profession' AND TG.type_id = aa.type_id
WHERE mv.mem_id = M.mem_Id) AS Profession
FROM tbl_members M
Brad beat me by 6 seconds
Just as an alternative is you want to stick to the joins:
SELECT M.mem_Id,
M.mem_email,
AA.att_value AS Gender,
AB.att_value AS Education,
AC.att_value AS Profession
FROM tbl_members M
JOIN tbl_mem_att_values mavA ON M.mem_Id = mavA.mem_id
JOIN tbl_mem_att_values mavB ON M.mem_Id = mavB.mem_id
JOIN tbl_mem_att_values mavC ON M.mem_Id = mavC.mem_id
JOIN tbl_types TA ON TA.type_name = 'Gender'
JOIN tbl_types TB ON TB.type_name = 'Education'
JOIN tbl_types TC ON TC.type_name = 'Profession'
LEFT JOIN tbl_attributes AA ON mavA.att_id = AA.att_id AND TA.type_id = AA.type_id
LEFT JOIN tbl_attributes AB ON mavB.att_id = AB.att_id AND TB.type_id = AB.type_id
LEFT JOIN tbl_attributes AC ON mavC.att_id = AC.att_id AND TC.type_id = AC.type_id
WHERE AA.type_id IN (TA.type_id, TB.type_id, TC.type_id)
AND AB.type_id IN (TA.type_id, TB.type_id, TC.type_id)
AND AC.type_id IN (TA.type_id, TB.type_id, TC.type_id)

MySQL query: list of tag.names and lang which isn't already inserted in combination inside images_urls

I can't figure out any good way to get a list of tag.names and lang which isn't already inserted in combination inside images_urls.
My database looks something like this.
tags
name user_id
+-------+---------+
|hi | 1 |
|friend | 1 |
|friend | 2 |
|people | 2 |
+-------+---------+
users
id lang
+---+------+
| 1 | en |
| 2 | sv |
+---+------+
images_urls
name lang
+--------+------+
| hi | en |
| friend | sv |
+--------+------+
What I would like to have returned would be:
result
name lang
+-------+------+
|friend | en |
|people | sv |
+-------+------+
I have tried something like this:
SELECT tags.name, users.lang
FROM tags, users
WHERE tags.user_id = users.id
AND CONCAT(tags.name, ',', users.lang) NOT IN(
SELECT DISTINCT CONCAT(name, ',', lang) FROM images_urls
)
GROUP BY CONCAT(name, ',', lang)
ORDER BY SUM(tags.count) DESC
LIMIT 20;
I'm not sure why images_urls has a name instead of a user_id, but this should work:
SELECT t.name, u.lang
FROM tags t
JOIN users u ON ( u.id = t.user_id )
LEFT JOIN images_urls iu ON ( iu.name=t.name AND iu.lang=u.lang )
WHERE iu.name IS NULL
Using the LEFT JOIN returns NULL for rows that do not exist in images_urls, so I check for that.
SELECT tags.name, users.lang
FROM tags, users
WHERE tags.user_id = users.id
AND users.name NOT IN (SELECT name from images_urls);
I would suggest images_urls should contain user_id not name, but thats a different issue.
column NOT IN ('name`','name1','name2','name3')
is also valid for testing purposes.

Resources