Left Joining table with values in lookup table - sql

I have two tables on SQL-Server. One containing clients, and one a client profile lookup table. So a bit like this (note that Fred doesn't have any values in the lookup table):
Table: Clients Table: Profile
ID | Name | Status ClientID | Type | Value
----------------------- -----------------------
1 | John | Current 1 | x | 1
2 | Peter | Past 1 | y | 2
3 | Fred | Current 2 | x | 3
2 | y | 4
I then am trying to create a tmp table that needs to contain all current clients like this:
ID | Name | TypeY
==================
1 | John | 2
3 | Fred |
My knowledge of SQL is limited, but I think I should be able to do this with a Left Join, so I tried this (#tmpClient is already created):
insert into #tmpClient
select a.ID, a.Name, b.Value
from Clients a
left join Profile b
on a.ID = b.ClientID
where a.Status = 'Current' and b.Type = 'y'
However this will always miss Fred out of the temporary table. I am probably doing something very simple wrong, but as I said I am missing the SQL skills to work this one out. Please can someone help me with getting this query right.

You have to move the predicate concerning the second table of the LEFT JOIN operation from WHERE to ON clause:
insert into #tmpClient
select a.ID, a.Name, b.Value
from Clients a
left join Profile b
on a.ID = b.ClientID and b.Type = 'y'
where a.Status = 'Current'

Related

Query table based on the value of another tables column

I have read-only access to a database and there are two tables that contain information I need. both tables have the same numbers in row a in referee to an account. I want to query the result of all accounts in table 1 that have "AD" in column B and where the account has values "4" in column C in of table 2. below is an example.
table 1 |
-------- |
A | B | |
_______ |
1 AC |
2 AD |
3 AC |
4 AD |
___________
table 2 |
-------- |
A | B | C |
__________|
1 AB 4 |
2 AB 5 |
3 AB 4 |
4 AB 4 |
I have tried the query
SELECT * FROM Table 1 WHERE column B = 'AD' and WHERE column C = '4' FROM TABLE 2
you can use inner Join instead,
Like this:
SELECT
t1.*
FROM
Table1 t1 JOIN Table2 t2 ON t1.A = t2.A
WHERE
t2.C = 4 AND t1.B = 'AD'
There isn't quite enough info here for me to help. There isn't common data between the two tables to link them.
Your query above is missing a join between the two tables, and you only need to state the where clause once, additional criteria can be added by using 'and.....'

SQL - Select with specific conditions in sub-query

I'm trying to use a query to select all the data of a company that answers a specific condition but I have trouble doing so. The following is what I have done so far:
SELECT *
FROM company a
WHERE a.id IN (SELECT b.company_id
FROM provider b
WHERE b.service_id IN (2, 4));
What I intend for the role of the sub-query (using the table below) to be, is to select the company_id that possess the service_id 2 and 4.
So in this example, it would only return the company_id 5:
+----------------+
| provider TABLE |
+----------------+
+----------------+----------------+----------------+
| id | company_id | service_id |
+--------------------------------------------------+
| 1 | 3 | 2 |
| 2 | 5 | 2 |
| 3 | 5 | 4 |
| 4 | 9 | 6 |
| 5 | 9 | 7 |
| ... | ... | ... |
As you may have guessed, the use of the IN in the sub-query does not fulfill my needs, it will select the company_id 5 but also the company_id 3.
I understand why, IN exists to check if a value matches any value in a list of values so it is not really what I need.
So my question is:
How can I replace the IN in my sub-query to select company_id
having the service_id 2 and 4?
The subquery should be:
SELECT b.company_id
FROM provider b
WHERE b.service_id IN (2, 4)
GROUP BY b.company_id
HAVING COUNT(b.service) = 2
You can self-JOIN the provider table to find companies that own both needed services.
SELECT p1.company_id
FROM provider p1
INNER JOIN provider p2 on p2.company_id = p1.company_id and p2.service_id = 2
WHERE p1.service_id = 4
As well as the other answers are correct if you want to see all company detail instead of only Company_id you can use two EXISTS() for each service_id.
SELECT *
FROM Company C
WHERE EXISTS (SELECT 1
FROM Provider P1
WHERE C.company_id = P1.company_id
AND P1.service_id = 2)
AND EXISTS (SELECT 1
FROM Provider P2
WHERE C.company_id = P2.company_id
AND P2.service_id = 4)
I offer this option for the subquery...
SELECT b.company_id
FROM provider b WHERE b.service_id = 2
INTERSECT
SELECT b.company_id
FROM provider b WHERE b.service_id = 4
Often, I find the performance of these operations to be outstanding even with very large data sets...
UNION
INTERSECT
EXCEPT (or MINUS in Oracle)
This article has some good insights:
You Probably Don't Use SQL Intersect or Except Often Enough
Hope it helps.

Comparing different columns in SQL for each row

after some transformation I have a result from a cross join (from table a and b) where I want to do some analysis on. The table for this looks like this:
+-----+------+------+------+------+-----+------+------+------+------+
| id | 10_1 | 10_2 | 11_1 | 11_2 | id | 10_1 | 10_2 | 11_1 | 11_2 |
+-----+------+------+------+------+-----+------+------+------+------+
| 111 | 1 | 0 | 1 | 0 | 222 | 1 | 0 | 1 | 0 |
| 111 | 1 | 0 | 1 | 0 | 333 | 0 | 0 | 0 | 0 |
| 111 | 1 | 0 | 1 | 0 | 444 | 1 | 0 | 1 | 1 |
| 112 | 0 | 1 | 1 | 0 | 222 | 1 | 0 | 1 | 0 |
+-----+------+------+------+------+-----+------+------+------+------+
The ids in the first column are different from the ids in the sixth column.
In a row are always two different IDs that are matched with each other. The other columns always have either 0 or 1 as a value.
I am now trying to find out how many values(meaning both have "1" in 10_1, 10_2 etc) two IDs have on average in common, but I don't really know how to do so.
I was trying something like this as a start:
SELECT SUM(CASE WHEN a.10_1 = 1 AND b.10_1 = 1 then 1 end)
But this would obviously only count how often two ids have 10_1 in common. I could make something like this for example for different columns:
SELECT SUM(CASE WHEN (a.10_1 = 1 AND b.10_1 = 1)
OR (a.10_2 = 1 AND b.10_1 = 1) OR [...] then 1 end)
To count in general how often two IDs have one thing in common, but this would of course also count if they have two or more things in common. Plus, I would also like to know how often two IDS have two things, three things etc in common.
One "problem" in my case is also that I have like ~30 columns I want to look at, so I can hardly write down for each case every possible combination.
Does anyone know how I can approach my problem in a better way?
Thanks in advance.
Edit:
A possible result could look like this:
+-----------+---------+
| in_common | count |
+-----------+---------+
| 0 | 100 |
| 1 | 500 |
| 2 | 1500 |
| 3 | 5000 |
| 4 | 3000 |
+-----------+---------+
With the codes as column names, you're going to have to write some code that explicitly references each column name. To keep that to a minimum, you could write those references in a single union statement that normalizes the data, such as:
select id, '10_1' where "10_1" = 1
union
select id, '10_2' where "10_2" = 1
union
select id, '11_1' where "11_1" = 1
union
select id, '11_2' where "11_2" = 1;
This needs to be modified to include whatever additional columns you need to link up different IDs. For the purpose of this illustration, I assume the following data model
create table p (
id integer not null primary key,
sex character(1) not null,
age integer not null
);
create table t1 (
id integer not null,
code character varying(4) not null,
constraint pk_t1 primary key (id, code)
);
Though your data evidently does not currently resemble this structure, normalizing your data into a form like this would allow you to apply the following solution to summarize your data in the desired form.
select
in_common,
count(*) as count
from (
select
count(*) as in_common
from (
select
a.id as a_id, a.code,
b.id as b_id, b.code
from
(select p.*, t1.code
from p left join t1 on p.id=t1.id
) as a
inner join (select p.*, t1.code
from p left join t1 on p.id=t1.id
) as b on b.sex <> a.sex and b.age between a.age-10 and a.age+10
where
a.id < b.id
and a.code = b.code
) as c
group by
a_id, b_id
) as summ
group by
in_common;
The proposed solution requires first to take one step back from the cross-join table, as the identical column names are super annoying. Instead, we take the ids from the two tables and put them in a temporary table. The following query gets the result wanted in the question. It assumes table_a and table_b from the question are the same and called tbl, but this assumption is not needed and tbl can be replaced by table_a and table_b in the two sub-SELECT queries. It looks complicated and uses the JSON trick to flatten the columns, but it works here:
WITH idtable AS (
SELECT a.id as id_1, b.id as id_2 FROM
-- put cross join of table a and table b here
)
SELECT in_common,
count(*)
FROM
(SELECT idtable.*,
sum(CASE
WHEN meltedR.value::text=meltedL.value::text THEN 1
ELSE 0
END) AS in_common
FROM idtable
JOIN
(SELECT tbl.id,
b.*
FROM tbl, -- change here to table_a
json_each(row_to_json(tbl)) b -- and here too
WHERE KEY<>'id' ) meltedL ON (idtable.id_1 = meltedL.id)
JOIN
(SELECT tbl.id,
b.*
FROM tbl, -- change here to table_b
json_each(row_to_json(tbl)) b -- and here too
WHERE KEY<>'id' ) meltedR ON (idtable.id_2 = meltedR.id
AND meltedL.key = meltedR.key)
GROUP BY idtable.id_1,
idtable.id_2) tt
GROUP BY in_common ORDER BY in_common;
The output here looks like this:
in_common | count
-----------+-------
2 | 2
3 | 1
4 | 1
(3 rows)

SQL - Compare 2 tables then add unique items to 3rd table

Title kind of says it all.
I have 2 tables Both with matching data
Table Name: Customers
_____________________________
ID | CompanyName
--------------
1 | Joes
2 | Kennys
3 | Kellys
4 | Ricks
5 | Johns
Table Name: OldCustomers
_____________________________
ID | CompanyName
--------------
1 | Joes
2 | Kennys
3 | Kellys
4 | Ricks
I want to do a comparison between the two tables. Then take the Row that doesn't exist in Table 2, and add it to a table i've created called "NewCustomers"
You can use the following:
insert into NewCustomers(id, companyname)
select c.id
, c.companyname
from Customers c
left join OldCustomers oc
on c.companyname = oc.companyname
where oc.id is null;
select *
from NewCustomers
See SQL Fiddle with Demo
This query will find all records where the company name is missing from the OldCustomers table. If you want to join on both id and company name you would just add and c.id = oc.id to the left join. It will give you the same results.

MySQL query help (involving joins?)

Although I've figured out several queries that almost do this, I can't quite get it perfectly and I'm getting frustrated. Here is the setup:
Table: Issue
| id | name | value |
+-------------------+
| 1 | a | 10 |
| 2 | b | 3 |
| 3 | c | 4 |
| 4 | d | 9 |
Table: Link
| source | dest |
+---------------+
| 1 | 2 |
| 1 | 3 |
The link table sets up a source/dest relationship between rows in the issue table. Yes, I know this is normalized terribly, but I did not create this schema even though I now have to write queries against it :(.
What I want is results that look like this:
| name | value |
+--------------+
| a | 17 |
| d | 9 |
The values in the results should be the sum of the values in the issue table when you aggregate together a source with all its dests along with the name of the source.
Some notes
(1) A source->dest is a one->many relationship.
(2) The best answer will not have any hardcoded id's or names in the query (meaning, it will be generalized for all setups like this).
(3) This is in MySQL
Thank you and let me know if I should include any more information
Its fairly simple, but the stickler is the fact that A is not a destination of A yet it is included in the table. The robust solution would involve modifying the data to add
Table: Link
| source | dest |
+---------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
Then a simple
SELECT a.name, SUM(d.value) FROM
Issues as a
JOIN Link as b on a.id=b.source
JOIN Issues AS d on b.dest=d.id;
GROUP BY a.name;
If you can't modify the data.
SELECT a.name, SUM(d.value)+a.value FROM
Issues as a
JOIN Link as b on a.id=b.source
JOIN Issues AS d on b.dest=d.id;
GROUP BY a.name,a.value;
MAY work.
SELECT S.name, S.value + SUM(D.value) as value
FROM Link AS L
LEFT JOIN Issue AS S ON L.source = S.id
LEFT JOIN Issue AS D ON L.dest = D.id
GROUP BY S.name
You could use a double join to find all linked rows, and add the sum to the value of the source row itself:
select src.name, src.value + sum(dest.value)
from Issue src
left join Link l
on l.source = src.id
left join Link dest
on dest.id = l.dest
group by src.name, src.value
This one should return the SUM of both source and dests, and only return items which are source.
SELECT s.name, COALESCE( SUM(d.value), 0 ) + s.value value
FROM Issue s
LEFT JOIN Link l ON ( l.source = s.id )
LEFT JOIN Issue d ON ( d.id = l.dest )
WHERE s.id NOT IN ( SELECT dest FROM Link )
GROUP BY s.name, s.value
ORDER BY s.name;