SQL: At least one value exists in another table - sql

I am trying to create a table that has columns called user_id and top5_foods (binary column). I currently have two tables, one has all of the user_ids and the foods associated with those user_ids and one table that only contains the top5 foods according to a type of calculation to select the top5 foods.
The table that I am trying to create if to have the column of the user_id and if at least one of their favorite foods is in the top_5_food table, put the value of the top5_foods as 1 and if not, 0.
Something like the following:
user_id top5_foods
----------------------
34223 1
43225 0
34323 1
I have tried to use the CASE command but it just duplicated the user_ids and mark 1 or 0 whenever it finds a food that is in the top_5_foods table. But I don't want it to duplicate. Could you please help ?
Thank you very much

If I understand correctly, a left join and aggregation:
select uf.user_id,
(count(t.food_id) > 0) as top5_foods
from user_foods uf left join
top5_foods t
on uf.food_id = t.food_id
group by uf.user_id;

Related

Multiplying fields from separate columns which have the same ID in SQL?

I have two tables which are joined by an ID...
table 1
- Assessment ID
- Module ID
- Assessment Weighting
table 2
- ID
- AssessmentID
- ModuleID
- UserID
- MarkFrom100
An assessment can have many students taking the assessment.
For example
A module has two assessments, one worth 60% and the other worth 40%. in table 2, I want to take the weighting value from table 1 and multiply it against the mark from 100.
SELECT * FROM Assessment, ModuleAssessmentUser WHERE
INNER JOIN moduleassementuser.assessmentID on Assessment.assessmentID
MULTIPLY AssessmentWeighting BY MarkFrom100 AS finalmark
UserID = 1
I know this is way off, but I really don't know how else to go about it.
My SQL knowledge is limited, so any help is appreciated!
You may use a SUM function in your query which will sum all the data of a certain group in a sub query wich will allow you to multiply the sum to the weight
sub query :
SELECT ModuleID, AssessmentID, UserID, SUM(MarkFrom100) as Total
FROM Table_2
GROUP BY ModuleID
Then use this sub query as a table in a main query :
SELECT T1.Assessment_ID, T1.ModuleID, Q1.UserID (Q1.Total * T1.Assessment_Weighting) as FinalMark
FROM (SELECT ModuleID, UserID, SUM(MarkFrom100) as Total
FROM Table_2
GROUP BY ModuleID) AS Q1
INNER JOIN Table_1 as T1 on T1.ModuleID = Q1.ModuleID
-- WHERE T1.ModuleID = 2 -- a particular module ID
GROUP BY ModuleID;
Note that the WHERE statement is in comment. If you want the whole data, remove it, if you want a particular data, use it ^^
NOTE :
I don't have your database, so it may need some tweeks, but the main idea is there

Querying SQL table with different values in same column with same ID

I have an SQL Server 2012 table with ID, First Name and Last name. The ID is unique per person but due to an error in the historical feed, different people were assigned the same id.
------------------------------
ID FirstName LastName
------------------------------
1 ABC M
1 ABC M
1 ABC M
1 ABC N
2 BCD S
3 CDE T
4 DEF T
4 DEG T
In this case, the people with ID’s 1 are different (their last name is clearly different) but they have the same ID. How do I query and get the result? The table in this case has millions of rows. If it was a smaller table, I would probably have queried all ID’s with a count > 1 and filtered them in an excel.
What I am trying to do is, get a list of all such ID's which have been assigned to two different users.
Any ideas or help would be very appreciated.
Edit: I dont think I framed the question very well.
There are two ID's which are present multiple time. 1 and 4. The rows with id 4 are identical. I dont want this in my result. The rows with ID 1, although the first name is same, the last name is different for 1 row. I want only those ID's whose ID is same but one of the first or last names is different.
I tried loading ID's which have multiple occurrences into a temp table and tried to compare it against the parent table albeit unsuccessfully. Any other ideas that I can try and implement?
SELECT
ID
FROM
<<Table>>
GROUP BY
ID
HAVING
COUNT(*) > 1;
SELECT *
FROM myTable
WHERE ID IN (
SELECT ID
FROM myTable
GROUP BY ID
HAVING MAX(LastName) <> MIN(LastName) OR MAX(FirstName) <> MIN(FirstName)
)
ORDER BY ID, LASTNAME

in sql how to return single row of data from more than one row in the same table

I have a single table of activities, some labelled 'Assessment' (type_id of 50) and some 'Counselling' (type_id of 9) with dates of the activities. I need to compare these dates to find how long people wait for counselling after assessment. The table contains rows for many people, and that is the primary key of 'id'. My problem is how to produce a result row with both the assessment details and the counselling details for the same person, so that I can compare the dates. I've tried joining the table to itself, and tried nested subqueries, I just can't fathom it. I'm using Access 2010 btw.
Please forgive my stupidity, but here's an example of joining the table to itself that doesn't work, producing nothing (not surprising):
Table looks like:
ID TYPE_ID ACTIVITY_DATE_TIME
----------------------------------
1 9 20130411
1 v 50 v 20130511
2 9 20130511
3 9 20130511
In the above the last two rows have only had assessment so I want to ignore them, and just work on the situation where there's both assessment and counselling 'type-id'
SELECT
civicrm_activity.id, civicrm_activity.type_id,
civicrm_activity.activity_date_time,
civicrm_activity_1.type_id,
civicrm_activity_1.activity_date_time
FROM
civicrm_activity INNER JOIN civicrm_activity AS civicrm_activity_1
ON civicrm_activity.id = civicrm_activity_1.id
WHERE
civicrm_activity.type_id=9
AND civicrm_activity_1.type_id=50;
I'm actually wondering whether this is in fact not possible to do with SQL? I hope it is possible? Thank you for your patience!
Sounds to me like you only want to get the ID numbers where you have a TYPE_ID entry of both 9 and 50.
SELECT DISTINCT id FROM civicrm_activity WHERE type_id = '9' AND id IN (SELECT id FROM civicrm_activity WHERE type_id = '50');
This will give you a list of id's that has entries with both type_id 9 and 50. With that list you can now go and get the specifics.
Use this SQL for the time of type_id 9
SELECT activity_date_time FROM civicrm_activity WHERE id = 'id_from_last_sql' AND type_id = '9'
Use this SQL for the time of type_id 50
SELECT activity_date_time FROM civicrm_activity WHERE id = 'id_from_last_sql' AND type_id = '50'
Your query looks OK to me, too. The one problem might be that you use only one table alias. I don't know, but perhaps Access treats the table name "specially" such that, in effect, the WHERE clause says
WHERE
civicrm_activity.type_id=9
AND civicrm_activity.type_id=50;
That would certainly explain zero rows returned!
To fix that, use an alias for each table. I suggest shorter ones,
SELECT A.id, A.type_id, A.activity_date_time,
B.type_id, B.activity_date_time
FROM civicrm_activity as A
JOIN civicrm_activity as B
ON A.id = B.id
WHERE A.type_id=9
AND B.type_id=50;

COUNT DISTINCT with CONDITIONS

I want to count the number of distinct items in a column subject to a certain condition, for example if the table is like this:
tag | entryID
----+---------
foo | 0
foo | 0
bar | 3
If I want to count the number of distinct tags as "tag count" and count the number of distinct tags with entry id > 0 as "positive tag count" in the same table, what should I do?
I'm now counting from two different tables where in the second table I've only selected those rows with entryID larger than zero. I think there should be a more compact way to solve this problem.
You can try this:
select
count(distinct tag) as tag_count,
count(distinct (case when entryId > 0 then tag end)) as positive_tag_count
from
your_table_name;
The first count(distinct...) is easy.
The second one, looks somewhat complex, is actually the same as the first one, except that you use case...when clause. In the case...when clause, you filter only positive values. Zeros or negative values would be evaluated as null and won't be included in count.
One thing to note here is that this can be done by reading the table once. When it seems that you have to read the same table twice or more, it can actually be done by reading once, in most of the time. As a result, it will finish the task a lot faster with less I/O.
This may work:
SELECT Count(tag) AS 'Tag Count'
FROM Table
GROUP BY tag
and
SELECT Count(tag) AS 'Negative Tag Count'
FROM Table
WHERE entryID > 0
GROUP BY tag
Try the following statement:
select distinct A.[Tag],
count(A.[Tag]) as TAG_COUNT,
(SELECT count(*) FROM [TagTbl] AS B WHERE A.[Tag]=B.[Tag] AND B.[ID]>0)
from [TagTbl] AS A GROUP BY A.[Tag]
The first field will be the tag the second will be the whole count the third will be the positive ones count.
I agree with #ntalbs solution,
if you want to count a column's data when the condition of another column's data is valid, you can do this
select
count(distinct tag) as tag_count,
count(distinct tag, case when entryId > 0 then tag end) as positive_tag_count
from
your_table_name;
On line 3, I added the column name beside the distinct, so it will count the distinct tags when the entryId is greater than 0
Code counts the unique/distinct combination of Tag & Entry ID when [Entry Id]>0
select count(distinct(concat(tag,entryId)))
from customers
where id>0
In the output it will display the count of unique values
Hope this helps
This may also work:
SELECT
COUNT(DISTINCT T.tag) as DistinctTag,
COUNT(DISTINCT T2.tag) as DistinctPositiveTag
FROM Table T
LEFT JOIN Table T2 ON T.tag = T2.tag AND T.entryID = T2.entryID AND T2.entryID > 0
You need the entryID condition in the left join rather than in a where clause in order to make sure that any items that only have a entryID of 0 get properly counted in the first DISTINCT.

SQL Precedence Query

I have a logging table which has three columns. One column is a unique identifier, One Column is called "Name" and the other is "Status".
Values in the Name column can repeat so that you might see Name "Joe" in multiple rows. Name "Joe" might have a row with a status "open", another row with a status "closed", another with "waiting" and maybe one for "hold". I would like to, using a defined precedence in this highest to lowest order:("Closed","Hold","Waiting" and "Open") pull the highest ranking row for each Name and ignore the others. Anyone know a simple way to do this?
BTW, not every Name will have all status representations, so "Joe" might only have a row for "waiting" and "hold", or maybe just "waiting".
I would create a second table named something like "Status_Precedence", with rows like:
Status | Order
---------------
Closed | 1
Hold | 2
Waiting | 3
Open | 4
In your query of the other table, do a join to this table (on Status_Precedence.Status) and then you can ORDER BY Status_Precedence.Order.
If you don't want to create another table, you can assign numeric precedence using a SELECT CASE
Select Name, Status, Case Status
When 'Closed' then 1
When 'Hold' then 2
When 'Waiting' then 3
When 'Open' Then 4
END
as StatusID
From Logging
Order By StatusId -- Order based on Case
A lookup table is also a good solution though.
I ended up using matt b's solution and using this final query to filter out the lower ranked (lower bing higher numbered).
SELECT * from [TABLE] tb
LEFT JOIN Status_Precedence sp ON tb.Status = sp.Status
WHERE sp.Rank = (SELECT MIN(sp2.rank)
FROM[Table] tb2
LEFT JOIN Status_Precedence sp2 ON tb2.Status = sp2.Status
WHERE tb.Status = tb2.Status)
order by tb.[name]