Excluding a value from a count with SQL - sql

I have two temp tables set up. Table A consists of members and the businesses that they manage, multiple members can be associated to a single business. Table B consists of just the members, their ID's, and the class of their business relationship (Retail, Business, or Retail and Business).
The query I need to come up with is to find out which of those members from Table B do not have a Retail relationship at all. Unfortunately a simple where clause will not suffice, because a member may have multiple relationships, i.e. John Doe has a Retail AND Business relationship, or possibly all three.
I can try SELECT * FROM B WHERE class='Business' which would pull all members who have Business relationships listed in the column, but on the flip side when I say WHERE class = 'Retail', it would bring in all those members who have a Business relationship as well. I want to exclude anyone from my count who doesn't have a retail relationship at all, so from my example above, John Doe would not be included.

I don't have any test data, but give this a try
Select
ta.*
From seequillTableA as ta
Left Join
(Select
ID
, COUNT(*) as cntRetail
From seequillTableB
Where Class <> 'Retail' AND Class <> 'Retail and Business'
Group By ID
Having COUNT(*) = 0
) as tb
On ta.ID = tb.ID
Where tb.cntRetail = 0

The relationships in Table B that are retail start with "Retail..." so we can select these using LIKE 'Retail%' then exclude them from the members we select from Table A by using NOT IN.
SELECT *
FROM TableA
WHERE MemberID NOT IN
(SELECT MemberID FROM TableB WHERE class LIKE 'Retail%')

Related

Create a additional row from join sql?

I have 2 tables: Person and House with 1-n relation.
I want to the result return as picture below:
Row always have a Person column with a null House column.
Thanks.
you can use unionall to join the result set with person table something like
select p.name,h.name as housename from person p join house h on p.id=h.personid
union all (select name,null from person)
order by name,housename

How do I do a look up from a user table where the user can be in two columns of the record?

This is a very simple idea and want to validate my approach.
I have a record that has the following:
RESERVATIONS
ID
OWNER
RESIDENT
1
VLL
MLL
2
MLL
CVLL
The lookup table looks like this:
USER_TABLE
PLID
USER_CD
BRANCH
1a
VLL
USA
2a
MLL
UK
I want to look up the value in the second table for owner and resident.
You cannot join the ID's together because they are not related. The only relation is from owner to USER_CD
Currently, I do the following:
Select CASE
when reservations.owner = 'VLL' then 1.user_cd
when reservations.owner = 'MLL' then 2.user_cd
end as 'Location'
FROM RESERVATIONS r
Join USER_TABLE 1
on RESERVATIONS.OWNER = 1.USER_CD
join USER_TABLE 2
ON RESERVATIONS.RESIDENT = 2.USER_CD
Is this a correct way to do it or is there another way?
Here is an example implementation:
https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=92f6178194a7e67f15652cbe7cc549c1
First, don't use numerals as table aliases. That's even worse than using arbitrary letters. The aliases should still Mean something to anyone reading or debugging the code.
Second, if you want to translate short branch names to long brnach names, the best way is to have another table with those lookups. In the code below I create that as an inline view (sub-query), though a real table with indexes would be Significantly better.
Then, I believe you're very close already...
WITH
branch_long_name(
name,
long_name
)
AS
(
SELECT 'USA', 'United States'
UNION ALL SELECT 'UK', 'United Kingdom'
-- Note; the UK and Great Britain are not the same
),
branch
AS
(
SELECT
t.*,
COALESCE(n.long_name, t.branch) AS long_name
FROM
user_table AS t
LEFT JOIN
branch_long_name AS n
ON n.name = t.branch
)
SELECT
r.*,
b_o.long_name AS owner_location,
b_r.long_name AS resident_location
FROM
reservations AS r
LEFT JOIN
branch AS b_o -- branch_owner
ON b_o.user_cd = r.owner
LEFT JOIN
branch AS b_r -- branch_resident
ON b_r.user_cd = r.resident
Demo : https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=7cc940ddf40e7cd085cff0fa02b65449

Selecting columns from different tables

I need to display two columns from my attendance table (MEMBER_ID & MEETING_ID) and one column from my meeting table and finally two columns from my member table which displays the names that match with MEETING_ID.
The attendance table has a composite key (MEMBER_ID*, MEETING_ID*)
The member table's primary key is MEMBER_ID
Meeting table's primary key is MEETING_ID
My attempt is not working, can someone please help?
SELECT MEMBER_ID, MEETING_ID, MEETING_NAME MEMBER_FIRSTNAME, MEMBER_LASTNAME
FROM ATTENDANCE, MEMBER, MEETING
WHERE MEETING.MEMBER_ID = MEETING.MEMBER_ID;
End result needs to be:
MEMBER_ID MEETING_ID MEETING_NAME FIRSTNAME LASTNAME
0001 MEET0004 SPORTS DAY JOHN SMITH
May be you need this.
SELECT A.MEMBER_ID, A.MEETING_ID, M2.MEETING_NAME, M1.MEMBER_FIRSTNAME, M1.MEMBER_LASTNAME
FROM ATTENDANCE A, MEMBER M1, MEETING M2
WHERE M1.MEMBER_ID = A.MEMBER_ID
AND A.MEETING_ID = M2.MEETING_ID;
SELECT
a.MEMBER_ID
,a.MEETING_ID
,mt.MEETING_NAME
,mb.MEMBER_FIRSTNAME
,mb.MEMBER_LASTNAME
FROM
ATTENDANCE a
INNER JOIN MEMBER mb
ON a.MEMBER_ID = mb.MEMBER_ID
INNER JOIN MEETING mt
ON a.MEETING_ID = mt.MEETING_ID
;
Use Explicit Join Syntax and then setup your relationships using the ON conditions and the keys between the tables. Note I also used table aliases to shorten typying.

Update multiple row values to same row and different columns

I was trying to update table columns from another table.
In person table, there can be multiple contact persons with same inst_id.
I have a firm table, which will have latest 2 contact details from person table.
I am expecting the firm tables as below:
If there is only one contact person, update person1 and email1. If there are 2, update both. If there is 3, discard the 3rd one.
Can someone help me on this?
This should work:
;with cte (rn, id, inst_id, person_name, email) as (
select row_number() over (partition by inst_id order by id) rn, *
from person
)
update f
set
person1 = cte1.person_name,
email1 = cte1.email,
person2 = cte2.person_name,
email2 = cte2.email
from firm f
left join cte cte1 on f.inst_id = cte1.inst_id and cte1.rn = 1
left join cte cte2 on f.inst_id = cte2.inst_id and cte2.rn = 2
The common table expression (cte) used as a source for the update numbers rows in the person table, partitioned by inst_id, and then the update joins the cte twice (for top 1 and top 2).
Sample SQL Fiddle
I think you don't have to bother yourself with this update, if you rethink your database structure. One great advantage of relational databases is, that you don't need to store the same data several times in several tables, but have one single table for one kind of data (like the person's table in your case) and then reference it (by relationships or foreign keys for example).
So what does this mean for your example? I suggest, to create a institution's table where you insert two attributes like contactperson1 and contactperson2: but dont't insert all the contact details (like email and name), just the primary key of the person and make it a foreign key.
So you got a table 'Person', that should look something like this:
ID INSTITUTION_ID NAME EMAIL
1 100 abc abc#inst.com
2 101 efg efg#xym.com
3 101 ijk ijk#fg.com
4 101 rtw rtw#rtw.com
...
And a table "Institution" like:
ID CONTACTPERSON1 CONTACTPERSON2
100 1 NULL
101 2 3
...
If you now want to change the email adress, just update the person's table. You don't need to update the firm's table.
And how do you get your desired "table" with the two contact persons' details? Just make a query:
SELECT i.id, p1.name, p1.email, p2.name, p2.email
FROM institution i LEFT OUTER JOIN person p1 ON (i.contactperson1 = p1.id)
LEFT OUTER JOIN person p2 ON (i.contactperson2 = p2.id)
If you need this query often and access it like a "table" just store it as a view.

Selecting distinct value pairs in EAV

I'm working on a user database where the profile data has been changed from a simple table into a Entity-Attribute-Value table.
Where as before the structure was along these lines:
userid (int)
address 1 (varchar)
city (varchar)
country (varchar)
It's now along these lines:
userid (int)
key (varchar)
value (varchar)
eg
userid key value
150 city London
150 country UK
151 city New York
151 country USA
152 country Mexico
I need to get a distinct list of city / country pairs and a count of all users for each country:
city country count
London UK 18
New York USA 25
There is no guarantee each key value pair will exist for each user, i.e there could be city, or country or both or neither as well as any number of other key values pairs.
This was straightforward with the old structure, but I can't even think how to begin on this, and would be grateful for some pointers
Your best solution is to go back to the traditional table because EAV makes most querying much harder than it should be - witness your problems here. You're going to be doing self-joins until you're sick of them, remanufacturing the table structure that allows you to perform sensible queries.
Cities and countries for each user ID:
SELECT a.userID, a.value AS city, b.value AS country
FROM EAV AS a
JOIN EAV AS b ON a.UserID = b.UserID
WHERE a.key = 'city'
AND b.key = 'country';
So, you end up with:
SELECT city, country, count(*)
FROM (SELECT a.userID, a.value AS city, b.value AS country
FROM EAV AS a
JOIN EAV AS b ON a.UserID = b.UserID
WHERE a.key = 'city'
AND b.key = 'country'
) AS c
GROUP BY city, country;
If there's a chance that someone might have two city or two country records, this will give you a Cartesian product with as many rows for that user as the product of the number of city and country records for that user.
This quite deliberately and consciously ignores users who have a city and no country or a country and no city (let alone those who have neither). Extending the solution to deal with those is only modestly painful - you end up with a 3-way UNION, I think, though you might be able to devise something with multiple left outer joins. But the fact that data can be entered into an EAV system without the necessary constraints to ensure that there is a city and a country for a user is simply one of the many reasons for rejecting EAV.
I'm sorry you had this foisted on you. I recommend looking at http://careers.stackoverflow.com/ as a way out of your pain, for this is only the beginning of it.
Dealing with users without either city or country or both. I think this will more or less do it:
SELECT a.userID, b.value AS city, c.value AS country
FROM (SELECT DISTINCT UserID FROM EAV) AS a
LEFT JOIN EAV AS b ON a.UserID = b.UserID
LEFT JOIN EAV AS c ON a.UserID = c.UserID
WHERE b.key = 'city'
AND c.key = 'country';
This should give you one record per user as long as there are no multiple city or country records for that user. The a scan gives you the list of unique user IDs that exist in the EAV table; the two outer joins give you the corresponding city or cities and corresponding country or countries for each such user ID, with nulls being generated if there is no city record or country record (or both) for the given user ID.
re: I need to get a distinct list of city / country pairs
SELECT DISTINCT country,city
FROM
(SELECT DISTINCT userid, VALUE AS country FROM TABLE WHERE KEY = 'country') country INNER JOIN
(SELECT DISTINCT userid, VALUE AS city FROM TABLE WHERE KEY = 'city') city ON
country.userid = city.userid
--count of all users for each country
SELECT VALUE AS country,
COUNT(DISTINCT userid) AS user_count
FROM TABLE
WHERE KEY = 'country'
GROUP BY
VALUE