Selectively retrieve data from tables when one record in first table is linked to multiple records in second table - sql

I have 2 tables:
1. Tbl_Master: columns:
a. SEQ_id
b. M_Email_id
c. M_location_id
d. Del_flag
2. Tbl_User: columns
a. U_email_id
b. Last_logged_date
c. User_id
First table Is master table it has unique rows i.e. single record of all users in the system.
Each User can be uniquely identified by the email_id in each table.
One user can have multiple profile, which means for one us_email_id field in the tblUser table, there can be many user_id in tbl_User,
i.e there can be multiple entries in second table for each user.
Now I have to select only those users who have logged in for last time before, lets say '2012', i.e before 1-Jan-2012.
But if one user has 2 or more user_id and one user_id has last_logged_date less than 2012
But other user_id has greater than 2012 then such user should be ignored.
In the last all all the result user will be marked for deletion by setting DEL_flag in master table to ‘Yes’
For eg:
Record in Tbl_Master:
A123 ram#abc.com D234 No
A123 john#abc.com D256 No
Record in tbl_User can be Like:
ram#abc.com '11-Dec-2011' Ram1
ram#abc.com '05-Apr-2014' Ram2
john#abc.com '15-Dec-2010' John1
In such case only John's Record should be selected not of Ram whose one profile has last_logged_date>1-Jan-2012

Another possibility was
SELECT
m.M_Email_id,
MAX(u.Last_logged_date) AS last_login
FROM
Tbl_Master m
INNER JOIN
Tbl_User u on u.U_email_id = m.M_Email_id
GROUP BY m.M_Email_id
HAVING
-- Year(MAX(u.Last_logged_date)) < 2012 -- use the appropriate function of your DBMS
EXTRACT(YEAR FROM(MAX(u.Last_logged_date))) < 2012 -- should be the version for oracle
-- see http://docs.oracle.com/cd/B14117_01/server.101/b10759/functions045.htm#i1017161
Your UPDATE operation can use this select in the WHERE clause.

Try this, this ans is in sql server, I haven't worked on Oracle.
select * from Tbl_Master
outer apply
(
select U_email_id,max(Last_logged_date)as LLogged,count(U_email_id) as RecCount
from Tbl_User
where Tbl_User.U_email_id = Tbl_Master.M_Email_id
group by U_email_id
)as a
where RecCount >2
and Year(LLogged) < '2012'
Try this DEMO
Hope it helps you.

Related

SQL SELECT WHERE IN another SELECT with GROUP_CONCAT

Good Day,
I have 3 Tables - Ticket, Ticket Batch (Multiple Ticket Rows To One Batch) and Ticket Staff (Multiple Staff Rows To One Ticket) and wish to ultimately UPDATE the ticket_batch table with the COUNT of all staff working on tickets per ticket batch.
The tables with applicable columns look as follows
ticket:
| ticket_number | recon_number |
ticket_batch:
| recon_number |
ticket_staff:
| ticket_number |
So I have written the following SQL query to essentially first if I do get the COUNT:
SELECT COUNT(*)
FROM ticket_staf
WHERE ticket_staff.ticket_number IN (SELECT GROUP_CONCAT(ticket.ticket_number) FROM ticket WHERE ticket.recon_number = 1);
Which the query just keeps running, but when I execute the queries separately:
SELECT GROUP_CONCAT(ticket.ticket_number)
FROM ticket
WHERE ticket.recon_number = 1;
I get 5 ticket numbers within split seconds and if I paste that string in the other portion of the query:
SELECT COUNT(*)
FROM ticket_staff
WHERE ticket_staff.ticket_number IN (1451,1453,1968,4457,4458);
It returns the correct COUNT.
So ultimately I guess can I not write queries with GROUP_CONCATS into another SELECT WHERE IN? And how should I structure my query?
Thanks for reading :)
I prefer Inner join as follows:
SELECT COUNT(distinct ts.*)
FROM ticket_staff ts
LEFT JOIN ticket t
ON ts.ticket_number = t.ticket_number
WHERE t.recon_number = 1;
GROUP_CONCAT() doesn't look right. I suspect you are confusing a list of values for IN with a string. They are not the same thing.
In general, I would recommend EXISTS over IN anyway:
SELECT COUNT(*)
FROM ticket_staff ts
WHERE EXISTS (SELECT 1
FROM ticket t
WHERE ts.ticket_number = t.ticket_number AND
t.recon_number = 1
);
For this query, you want an index on ticket(ticket_number, recon_number). However, I am guessing that ticket(ticket_number) is the primary key, which is enough of an index by itself.

SQL: At least one value exists in another table

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;

SQL count, use only last record

can someone help me about counting rows in sql. I have a table, archive, in which I have bank account and status of that account. One account can have and usually have more records, in my count I have to use last record, not records before. Example:
account status
5552222 A
5552222 B
5552222 A
**5552222 B**
4445896 A
4445896 B
**4445896 A**
I have to use this who are bold. Based on this there is one B(blocked) and one A(active) account. I have column datetime, which can tell me what is last record. I just need query to count that
Assuming you want to count based on the most current row for an account:
SELECT tab.status,
COUNT(*)
FROM tab JOIN
(
SELECT account, MAX(datetime) AS maxdate
FROM tab
GROUP BY account
) AS dt
ON tab.account = dt.account
AND tab.datetime = dt.maxtime
GROUP BY tab.Status
SELECT COUNT(*)
FROM yourTable
WHERE Status='B'
or
WHERE AccountName LIKE '%B'
Edit: After OP modified the question to include the table data.
So, the problem is that the same account number can occur multiple times, and you want to count on the basis of last status of the account.
If the account is currently blocked, you would like to count it, irrespective of the number of times it gets blocked earlier.
Assumption: You have a date type column in your table which shows the date when the record's (with new status value) was inserted (or it may be an identity field which keeps track of the order of records created in the table)
The query will be:
SELECT COUNT (*)
FROM
(
SELECT DISTINCT
acNumber,
( SELECT Max(identityField_or_dateField)
FROM tableName t
WHERE t.acNumber = t2.acNumber AND Status='B')
FROM tableName t2
WHERE
( SELECT Max(identityField_or_dateField)
FROM tableName t
WHERE t.acNumber = t2.acNumber AND Status='B') IS NOT NULL
) tblAlias
Glad to help! Please remember to accept the answer if you found it helpful.

How to find out the duplicate records

Using Sql Server 2000
I want to find out the duplicate record in the table
Table1
ID Transaction Value
001 020102 10
001 020103 20
001 020102 10 (Duplicate Records)
002 020102 10
002 020103 20
002 020102 10 (Duplicate Records)
...
...
Transaction and value can be repeat for different id's, not for the same id...
Expected Output
Duplicate records are...
ID Transaction Value
001 020102 10
002 020102 10
...
...
How to make a query for view the duplicate records.
Need Query help
You can use
SELECT
ID, Transaction, Value
FROM
Table1
GROUP BY
ID, Transaction, Value
HAVING count(ID) > 1
Select Id, Transaction, Value, Count(id)
from table
group by Id, Transaction, Value
having count(id) > 1
This query will show you the count of times the ID has been repeated with each entry of the Id. If you don't need it you can simply remove the Count(Id) column from the select clause.
Self join (with additional PK or Timestamp or...)
I can see that people've provided solution with grouping but none has provided the self join solution. The only problem is that you'd need some other row descriptor that should be unique for each record. Be it primary key, timestamp or anything else... Suppose that the unique column's name is Uniq this would be the solution:
select distinct ID, [Transaction], Value
from Records r1
join Records r2
on ((r2.ID = r1.ID) and
(r2.[Transaction] = r1.[Transaction]) and
(r2.Value = r1.Value) and
(r2.Uniq != r1.Uniq))
The last join column makes it possible to not join each row to itself but only to other duplicates...
To find out which one works best for you, you can check their execution plan and execute some testing.
You can do this:
SELECT ID, Transaction, Value
FROM Table
GROUP BY ID, Transaction, Value
HAVING COUNT(*) > 1
To delete the duplicates, if you have no primary key then you need to select the distinct values into a separate table, delete everything from this one, then copy the distinct records back:
SELECT ID, Transaction, Value
INTO #tmpDeduped
FROM Table
GROUP BY ID, Transaction, Value
DELETE FROM Table
INSERT Table
SELECT * FROM #tmpDeduped

SQL Server 2005 - How to take a record from one table and loop through another table looking for match

I have 2 tables. for this example I will use only one users records.
The first table has the user name and an evaluation date as such:
USER EVALDATE
--------------
bobr 6/7/2010
bobr 9/20/2010
bobr 9/21/2010
The above table needs to be joined against this user history table, which has the history of the ID's and the dates they were valid, to look for a match (the NULL date means current):
USER STARTDATE ENDDATE
----------------------------
bobr 2/20/2006 4/18/2010
bobr2 4/19/2010 9/7/2010
bobr 9/8/2010 null
What I'm trying to do in SQL Server 2005 is take the first record from the first table, loop it through the second table and when(if) the EVALDATE is within one of these date ranges and the IDs match, then flag that record from the first table as valid.
The current code takes the record from the first table and runs against all rows of the second table and kicks out a record for each invalid evaldate, so it kicks out a record when joined against the second table because the evaldate is not between the dates of the first record on the history table, even though the record is fine because the evaldate is between the start and end dates of the third record in the history table.
I hope this makes sense! In something like SAS I can create an array and loop through checking against each record in the history table. How do I do this in SQL? What I was trying to do was just update the first table with a flag if the records dates are invalid. Any ideas? Thanks!!!
Try this:
SELECT [USER]
,[EVALDATE]
,CASE WHEN ( SELECT COUNT(*)
FROM [UserStartEndDates] b
WHERE [a].[USER] = [b].[User]
AND [EVALDATE] BETWEEN [STARTDATE]
AND COALESE([ENDDATE],[EVALDATE])
) > 0 THEN 1
ELSE 0
END AS [IsValid]
FROM [Evaluations] a
You can try something like
Select *
FROM Users u INNER JOIN
UserHistory uh ON u.User = uh.User
AND u.EvalDate BETWEEN uh.StartDate
AND ISNULL(uh.EndDate, u.EvalDate)
EDIT
Try this for all values from User
Select u.*,
CASE
WHEN uh.User IS NULL
THEN 'Invalid'
ELSE 'Valid'
END Validity
FROM Users u LEFT JOIN
UserHistory uh ON u.User = uh.User
AND u.EvalDate BETWEEN uh.StartDate
AND ISNULL(uh.EndDate, u.EvalDate)
try something like:
select * from [table2] t2
join [table1] t1 on t1.user = t2.user
--or better yet the foreign key
where t1.user = t2.user and t1.evaldate
between t2.startdate and t2.enddate