exclude some ids associated to determined condition - sql

In the following code I am looking forward to extract the ids associated to accounts with a determined status.
There are 4 status (Active, Inactive, Closed, Verifying)
Almost every account is associated to a phone and one phone could be associated to various accounts, for example:
A phone could be associated to various accounts, 1 Active, 1 inactive and let's say maybe another closed (lets call this account A).
Account B is only associated to a Closed account
Account C is only associated to an Inactive account
With my code I am looking to identify those phones who are ONLY associated to accounts that are not active. I noticed that my code gives me back all phones associated to accounts like A, B and C (Because A is associated to at least 1 NOT 'Active' account), I just want phones associated to accounts like B and C, and exclude ALL type A (if the phone is linked to at least 1 Active account, I don't want it in my db)
select
phone.id,
phone.number,
account.status as status_account
max(date_trunc('day', accountstatuslog.modified)::date) as modif_account_date
from accountstatuslog
join account on account.id = accountstatuslog.account_id
join phone on phone.id = account.phone_id
where
account.status not in ('Active') and
accountstatuslog.status = account.status and
group by
account.status,
account.phone_id

You can use Anti-Join to eliminate the records that you want.
Anti-Join is efficient because it rejects a row at the first occurrence of the value to eliminate.
Table:
postgres=# select * from PhoneStatus;
phoneaccholder | accountstatus
----------------+---------------
Vincent | Active
Vincent | Inactive
Xavier | Active
Benjamin | Closed
Sebastian | Closed
Gabriel | Active
Christopher | Inactive
(7 rows)
SQL:
SELECT phoneaccholder
,accountstatus
-- ,Count(*)
FROM phonestatus x
WHERE NOT EXISTS (SELECT 1
FROM phonestatus y
WHERE y.accountstatus = 'Active'
AND x.phoneaccholder = y.phoneaccholder)
and accountstatus in ('Closed','Inactive')
GROUP BY accountstatus,
phoneaccholder;
Output:
phoneaccholder | accountstatus
----------------+---------------
Benjamin | Closed
Sebastian | Closed
Christopher | Inactive
(3 rows)

Related

SQL - How to get a max value from a table and add it into another (sqlite3)

Just like the title says, how would i get the maximun value from one table and add it into a field into another table from the same database:
I currently have my main table "users":
username | password | Email | Highscore 1 | Highscore 2 | Highscore 3 |
I also have my other tables :
"user_scores1":
username | Score 1 |
"user_scores2":
username | Score 2 |
"user_scores3":
username | Score 3 |
The "user_scores" tables contains all the scores of all the users (for the 3 different game modes) whenever they play. Whenever the user finishes the game for a particular game mode, a new score gets added into a new row as well as their username associaed to it, to the table of scores for that gamemode
I want to filter out all the scores from a user (e.g user1) and then get their highest score from the game modes, (e.g filtering out all the scores of user1 from the user_scores1 table)
With this, i want to get the highest score of that specific user from that specific table , and add it into my main table "users" in the appropite field (e.g like the previous example ,filtering out all the scores of user1 from the user_scores1 table, then getting the highest score and adding that score into my main table "users" into highscores1 where the username is user1 )
Is this what you want?
update users
set highscore1 = (select max(score) from user_scores1 us where us.username = users.name),
highscore2 = (select max(score) from user_scores2 us where us.username = users.name),
highscore3 = (select max(score) from user_scores3 us where us.username = users.name);

How to create two JOIN-tables so that I can compare attributes within?

I take a Database course in which we have listings of AirBnBs and need to be able to do some SQL queries in the Relationship-Model we made from the data, but I struggle with one in particular :
I have two tables that we are interested in, Billing and Amenities. The first one have the id and price of listings, the second have id and wifi (let's say, to simplify, that it equals 1 if there is Wifi, 0 otherwise). Both have other attributes that we don't really care about here.
So the query is, "What is the difference in the average price of listings with and without Wifi ?"
My idea was to build to JOIN-tables, one with listings that have wifi, the other without, and compare them easily :
SELECT avg(B.price - A.price) as averagePrice
FROM (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 0
) A, (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 1) B
WHERE A.id = B.id;
Obviously this doesn't work... I am pretty sure that there is a far easier solution to it tho, what do I miss ?
(And by the way, is there a way to compute the absolute between the difference of price ?)
I hope that I was clear enough, thank you for your time !
Edit : As mentionned in the comments, forgot to say that, but both tables have idas their primary key, so that there is one row per listing.
Just use conditional aggregation:
SELECT AVG(CASE WHEN a.wifi = 0 THEN b.price END) as avg_no_wifi,
AVG(CASE WHEN a.wifi = 1 THEN b.price END) as avg_wifi
FROM Billing b JOIN
Amenities a
ON b.id = a.id
WHERE a.wifi IN (0, 1);
You can use a - if you want the difference instead of the specific values.
Let's assume we're working with data like the following (problems with your data model are noted below):
Billing
+------------+---------+
| listing_id | price |
+------------+---------+
| 1 | 1500.00 |
| 2 | 1700.00 |
| 3 | 1800.00 |
| 4 | 1900.00 |
+------------+---------+
Amenities
+------------+------+
| listing_id | wifi |
+------------+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 0 |
+------------+------+
Notice that I changed "id" to "listing_id" to make it clear what it was (using "id" as an attribute name is problematic anyways). Also, note that one listing doesn't have an entry in the Amenities table. Depending on your data, that may or may not be a concern (again, refer to the bottom for a discussion of your data model).
Based on this data, your averages should be as follows:
Listings with wifi average $1600 (Listings 1 and 2)
Listings without wifi (just 3) average 1800).
So the difference would be $200.
To achieve this result in SQL, it may be helpful to first get the average cost per amenity (whether wifi is offered). This would be obtained with the following query:
SELECT
Amenities.wifi AS has_wifi,
AVG(Billing.price) AS avg_cost
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
which gives you the following results:
+----------+-----------------------+
| has_wifi | avg_cost |
+----------+-----------------------+
| 0 | 1800.0000000000000000 |
| 1 | 1600.0000000000000000 |
+----------+-----------------------+
So far so good. So now we need to calculate the difference between these 2 rows. There are a number of different ways to do this, but one is to use a CASE expression to make one of the values negative, and then simply take the SUM of the result (note that I'm using a CTE, but you can also use a sub-query):
WITH
avg_by_wifi(has_wifi, avg_cost) AS
(
SELECT Amenities.wifi, AVG(Billing.price)
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
)
SELECT
ABS(SUM
(
CASE
WHEN has_wifi = 1 THEN avg_cost
ELSE -1 * avg_cost
END
))
FROM avg_by_wifi
which gives us the expected value of 200.
Now regarding your data model:
If both your Billing and Amenities table only have 1 row for each listing, it makes sense to combine them into 1 table. For example: Listings(listing_id, price, wifi)
However, this is still problematic, because you probably have a bunch of other amenities you want to model (pool, sauna, etc.) So you might want to model a many-to-many relationship between listings and amenities using an intermediate table:
Listings(listing_id, price)
Amenities(amenity_id, amenity_name)
ListingsAmenities(listing_id, amenity_id)
This way, you could list multiple amenities for a given listing without having to add additional columns. It also becomes easy to store additional information about an amenity: What's the wifi password? How deep is the pool? etc.
Of course, using this model makes your original query (difference in average cost of listings by wifi) a bit tricker, but definitely still doable.

SQL - Tracking student exam records as they move between schools

I'd like to pick some of your glorious minds for an optimal solution to my dilemma.
Scenario:
Schools have children and children take tests.
The tests point to the child, not the school.
If the child moves school, the test records are taken to the new school and the previous school has no record of the test being done as they are linked to the child.
Obviously, this isn't ideal and is the result of the database not being designed with this in mind. What would the correct course of action be; I’ve currently identified the 3 possibilities listed below which would solve the current problem. However, i cannot be sure which is best for the issue at hand - and if any better solutions exist.
Have each test store the school & student within the test records (requiring current records to be updated & increasing the size of the database)
Create a new child record, duplicating the existing data for the new school with a new ID so the test remains linked to the previous school (complicating the ability to identify previous test scores)
Separately keep track of moves to other schools, then use this additional table to identify current and previous using the timestamps (increased complexity and computational requirements)
EDIT:
So i tried to use a basic example, but requests for the task at hand have been requested.
Here's the DB Schema for the tables (simplified for problem, note: Postnatal is not important):
Patients: ID, MidwifeID, TeamID
Midwives: ID
Groups: ID
GroupsMidwives: MidwifeID, GroupsID
PatientObservations: ID, MidwifeID, PatientID
Using a query as follows:
SELECT Some Information
from Postnatals
JOIN Midwives on Postnatals.MidwifeID = Midwives.ID
JOIN Patients on Patients.PatientID = Postnatals.PatientID
JOIN GroupsMidwives on GroupsMidwives.MidwifeID = Midwives.ID
JOIN Groups on Groups.ID = GroupsMidwives.GroupID
JOIN PatientObservations on PatientObservations.PatientID =
Postnatals.PatientID
WHERE groups.Name = ?
*some extra checks*
GROUP BY Midwives.Firstname, Midwives.Surname, Midwives.ID
However, in the event that a midwife is moved to a different team, the data associated with the previous team is now owned by the newly assigned team. As described in the example detailed previously.
Thus a modification (which modification is yet to be realised) is required to make the data submitted - prior to a team change - assigned to the previous team, as of current, because of the way the records are owned by the midwife, this is not possible.
You should below suggestion as per you concern.
Step 1 ) You need to create School Master Table
ID | School | IsActive
1 | ABC | 1
2 | XYZ | 1
Step 2 ) You need to create Children Master having school id as foreign key
ID | School | Children Name| IsActive
1 | 2 | Mak | 1
2 | 2 | Jak | 1
Step 3 ) You need to create test table having children id as foreign key
ID | Children_id | Test Name | IsActive
1 | 2 | Math | 1
2 | 2 | Eng | 1
Now whenever child moves school then make child record inactive and create another active record with new school. This will help you to bifurcate the old test and new test.
do let me know in case morehelp required

Items getting double-counted in SQL Server, dependent counting logic not working right

I am counting the number of RFIs (requests for info) from various agencies. Some of these agencies are also part of a task force (committee). Currently this SQL combines the agencies and task forces into one list and counts the RFIs for each. The problem is, if the RFI belongs to a task force (which is also assigned to an agency), I only want it to count for the task force and not for the agency. However, if the agency does not have a task force assigned to the RFI, I want it to still count for the agency. The RFIs are linked to various agencies through a _LinkEnd table, but that logic works just fine. Here is the logic thus far:
SELECT t.Submitting_Agency, COUNT(DISTINCT t.Count) AS RFICount
FROM (
SELECT RFI_.Submitting_Agency, RFI_.Unique_ID, _LinkEnd.EntityType_ID1, _LinkEnd.Link_ID as Count
FROM RFI_
JOIN _LinkEnd ON RFI_.Unique_ID=_LinkEnd.Entity_ID1
WHERE _LinkEnd.Link_ID LIKE 'CAS%' AND RFI_.Date_Submitted BETWEEN '20110430' AND '20110630'
UNION ALL
SELECT RFI_.Task_Force__Initiative AS Submitting_Agency, RFI_.Unique_ID, _LinkEnd.EntityType_ID1, _LinkEnd.Link_ID as Count
FROM RFI_
JOIN _LinkEnd ON RFI_.Unique_ID=_LinkEnd.Entity_ID1
WHERE _LinkEnd.Link_ID LIKE 'CAS%' AND RFI_.Date_Submitted BETWEEN '20110430' AND '20110630' AND RFI_.Task_Force__Initiative IS NOT NULL) t
GROUP BY t.Submitting_Agency
How can I get it to only count an RFI one time, even though the two fields are combined? For instance, here are sample records from the RFI_ table:
---------------------------------------------------------------------------
| Unique_ID | Submitting_Agency | Task_Force__Initiative | Date_Submitted |
---------------------------------------------------------------------------
| 1 | Social Service | Flood Relief TF | 2011-05-08 |
---------------------------------------------------------------------------
| 2 | Faith-Based Init. | Homeless Shelter Min. | 2011-06-08 |
---------------------------------------------------------------------------
| 3 | Psychology Group | | 2011-05-04 |
---------------------------------------------------------------------------
| 4 | Attorneys at Law | | 2011-05-05 |
---------------------------------------------------------------------------
| 5 | Social Service | | 2011-05-10 |
---------------------------------------------------------------------------
So assuming only one link existed to one RFI for each of these, the count should be as follows:
Social Service 1
Faith-Based Unit. 0
Psychology Group 1
Attorneys at Law 1
Flood Relief TF 1
Homeless Shelter Min. 1
Note that if both an agency and a task force are in one record, then the task force gets the count, not the agency. But it is possible for the agency to have a record without a task force, in which case the agency gets the count. How could I get this to work in this fashion so that RFIs are not double-counted? As it stands both the agency and the task force get counted, which I do not want to happen. The task force always gets the count, unless that field is blank, then the agency gets it.
I guess a simple COLESCE() would do the trick?
SELECT COLAESCE(Task_Force__Initiative, Submitting_Agency), COUNT(DISTINCT _LinkEnd.Link_ID) AS RFICount
FROM RFI_
JOIN _LinkEnd ON RFI_.Unique_ID=_LinkEnd.Entity_ID1
WHERE _LinkEnd.Link_ID LIKE 'CAS%' AND RFI_.Date_Submitted BETWEEN '20110430' AND '20110630'
GROUP BY COLAESCE(Task_Force__Initiative, Submitting_Agency);
Rather than:
SELECT t.Submitting_Agency ...
Try
SELECT
CASE t.[Task_Force__Initiative]
WHEN NULL THEN -- Or whatever value constitutes "empty"
t.[Submitting_Agency]
ELSE
t.[Task_Force__Initiative]
END ...
and then GROUP BY the same.
http://msdn.microsoft.com/en-us/library/ms181765.aspx
The result will be that your count will aggregate from the proper specified grouping point, rather than from the single agency column.
EDIT: From your example it looks like you don't use NULL for the empty field but maybe a blank string? In that case you'll want to replace the NULL in the CASE above with the proper "blank" value. If it is NULL then you can COALESCE as suggested in the other answer.
EDIT: Based on what I think your schema is... and your WHERE criteria
SELECT
COALESCE(RFI_.[Task_Force__Initiative], RFI_.[Submitting_Agency]),
COUNT(*)
FROM
RFI_
JOIN _LinkEnd
ON RFI_.[Unique_ID]=_LinkEnd.[Entity_ID1]
WHERE
_LinkEnd.[Link_ID] LIKE 'CAS%'
AND RFI_.[Date_Submitted] BETWEEN '20110430' AND '20110630'
GROUP BY
COALESCE(RFI_.[Task_Force__Initiative], RFI_.[Submitting_Agency])

Tough SQL Update

2 databases QF AND TK
QF has the following:
Imagine you have a table called FunctionalGroup with this data:
FunctionalGroupID | FunctionalGroup
1 Engineering
2 Purchasing
And a table that was a set of login's with a functionalgroupID to reference the group the person is in...
LoginID | FunctionalGroupID | Login
1 1 Jon
2 1 Joe
3 2 Jane
So Jon and Joe are engineering while Jane is purchasing..simple enough
Now there is another database TK.
TK has the following table Login with something to this effect:
Login | FunctionalGroupID
Jon Purchasing
Joe Purchasing
Jane Purchasing
Notice how Jon and Joe in this database are now part of the purchasing group...But notice how this field is the text field and no ID. So what I want to do is use this table as the master data source and update the QF table such that the logins table from the QF now looks like this:
LoginID | FunctionalGroupID | Login
1 2 Jon
2 2 Joe
3 2 Jane
That is update this table to make Jon and Joe part of the purchasing group by setting their functionalgroupid = 2. Because 2 means purchasing.
I tried this:
UPDATE
Login
SET Login.FunctionalGroupID = FunctionalGroup.FunctionalGroupID
FROM Login INNER JOIN
TKKCommonData.dbo.Login lz
ON lz.Login = Login.Login
AND lz.FunctionalGroupID = FunctionalGroup.FunctionalGroup
But I get an error:
Msg 4104, Level 16, State 1, Line 1
The multi-part identifier "FunctionalGroup.FunctionalGroup" could not be bound.
This seems so easy but Im just not sure how to write the update statement. Im just looking to join the tables by the Login (which is the users name) and then by the Functionalgroup names.
I even tried this EDIT per Jay's answer with same error message
UPDATE
QuikFix.dbo.Login
SET QuikFix.dbo.Login.FunctionalGroupID = QuikFix.dbo.FunctionalGroup.FunctionalGroupID
FROM QuikFix.dbo.Login INNER JOIN
TKKCommonData.dbo.Login
ON TKKCommonData.dbo.Login.Login = QuikFix.dbo.Login.Login
AND TKKCommonData.dbo.Login.FunctionalGroupID = QuikFix.dbo.FunctionalGroup.FunctionalGroup
WHERE TKKCommonData.dbo.Login.LoginID= 101
You need an additional INNER JOIN:
UPDATE Login
SET
Login.FunctionalGroupID = FunctionalGroup.FunctionalGroupID
FROM Login
INNER JOIN TKKCommonData.dbo.Login lz
ON lz.Login = Login.Login
INNER JOIN FunctionalGroup
ON lz.FunctionalGroupID = FunctionalGroup.FunctionalGroup
Specify the database name for all of the tables in your query instead of just TKKCommonData.dbo.Login, seems like it can't find the FunctionalGroup table in the database the query is running against.