Join 2 tables to find out how many open incidents a customer has open - sql-server-2008

I'm doing a piece of work that has highlighted a number of accounts in our internal system that have been duplicated. To identify the duplicates, I've created the following script:
select SAMAccountName, COUNT(*)
from dbo.customer
group by SAMAccountName
having COUNT(*) > 1
order by SAMAccountName asc
The NULL accounts need to be ignored as they are related to PowerShell scripts being used currently.
On the back of this, I need to find out how many open incidents these duplicates have on our system. This is where I have to dip into the Incident table. I'd like to bring back the following columns from Incident...
select customerdisplayname, customeremail, status
from dbo.incident
The Status of the incident CANNOT be Resolved or Closed.
The CustomerDisplayName field in the Incident table is the same as FullName in the Customer table. Not sure if this will be needed in the script.
Any help you have on this would be much appreciated.

From your description, and assuming that duplicate SAMAccountName values could have differing fullname values, then the following seems appropriate:
select t.SAMAccountName, i.customerdisplayname, i.customeremail, i.status
from
(
select SAMAccountName
from dbo.customer
group by SAMAccountName
having count(*) > 1
) t
inner join dbo.customer c on t.SAMAccountName = c.SAMAccountName
inner join dbo.incident i on c.fullname = i.customerdisplayname
where i.status <> 'Resolved' and i.status <> 'Closed'

Related

MYSQL count listings for each user where listing published in past

I have a MYSQL query that I am having difficulties getting to do what I want.
I have a users table (userstbl) containing all my user records, and a listings table (listings) contains all listings posted by each user. I am trying to select the name and address of each user and provide a count of listings for each user which was listed between a certain date range, but only count adverts for unique category_id's which is working fine.
The issue is that I only want to count listings that have been published. I have another table which is identical to my listings table called "listings_log" and contains a record for every change made to every listing record. If one of the records in "listings_log" for the listing has a "listings_log.published=1" than the listing was published. Each record in the "listings_log" table has a "listing_id" which is the same as in the "listings" table.
This is the query I have now :
SELECT
userstbl.userid,
userstbl.fullname,
userstbl.fulladdress,
COUNT(DISTINCT(
CASE WHEN listings.ad_type = 1
AND DATE(listings.date_listed) BETWEEN '2018-01-01' AND '2018-04-01'
THEN listings.category_id
END )
) AS Listings_Count_2018,
DATE_FORMAT(userstbl.reg_date, "%d/%m/%Y") AS RegisteredDate
FROM
users
LEFT JOIN listings ON listings.userid = userstbl.user_id
GROUP BY userstbl.userid
This counts the number of unique listings records between the correct dates for each user.
But I somehow only need to count listings records, where there is a corresponding listings_log record for that listing with published set to "1". The "listings_log" table and "listings" table both have a common listing_id column, but the listings_log table can have multiple records for each listing showing every change to each listing.
So I want to also join on the listings_log.listing_id = listings.listing_id and at least one of the "listings_log" records for that "listing_id" has listings_log.published = "1".
As you did not provide sample tables and a minimal reproducible example, a lot of this is guesswork. I am assuming for each user you want the total number of listing records. I built up the SQL with subqueries that are meant to be read "from the inside out."
select u.userid, u.fullname, u.fulladdress, sq.count from usertbl u join (
select u.userid, sum(c.count) as count from usertbl u join (
select count(*) as count, l.userid, l.listing_id from listings l join (
select distinct listing_id from listings_log where listings_log.published = "1"
) ll on l.listing_id = ll.listing_id
and l.ad_type = 1
and date(l.date_listed) between '2018-01-01' and '2018-04-01'
group by l.userid, l.listing_id
) c on u.userid = c.userid
group by u.userid
) sq on u.userid = sq.userid
;
See DB Fiddle

grouping records conditionally

My Google-fu is coming up short on this one. I've got a table of transactions, like this:
id email source amount timestamp
1 daniel#example.com vendor 10 2014-03-10 23:34:40
2 john#example.com website 15 2014-03-11 13:30:00
3 mary#example.com website 50 2014-03-11 17:30:00
4 daniel#example.com website 65 2014-03-13 20:06:30
5 mary#example.com vendor 10 2014-03-14 16:20:30
I want to be able to group these by email, but only for users who:
A) came in through the 'vendor' source initially, and
B) also made a transaction through the 'website' source.
So for the above sample data, I would want this:
email total_amount transactions
daniel#example.com 75 2
Mary would not be included because her first transaction was through 'website', and not 'vendor'. John would not be included because he did not have a transaction through the vendor at all.
EDIT:
Less ideal, but still useful, would be this result set:
email total_amount transactions
daniel#example.com 75 2
mary#example.com 60 2
Where Mary and Daniel are both included because they both came in through the 'vendor' source in at least one transaction.
SELECT A.Email, sum(B.Amount) as Total_Amount, count(B.time) as Transactions
FROM tableName A
INNER join tableName B
on A.Email=B.Email
AND A.source='vendor'
Group By A.Email
Requirements are a bit unclear as you initially indicate the must initially come though vendor, but then you retract that statement later by adding mary.
http://sqlfiddle.com/#!2/bb4f9/1/0
If date/timestamps are important add an AND clause for A.Time<= B.Time and aggregrate the A.Amoun t and A.time and add those in like...
SELECT A.Email, sum(B.Amount)+ sum(A.Amount) as Total_Amount, count(B.time)+count(A.Time) as Transactions
FROM tableName A
INNER join tableName B
on A.Email=B.Email
AND A.source='vendor'
and A.Time<=B.Time
Group By A.Email
But this assumes vendor entry will only occur once for each email
So this solution first finds a vendor entry (if there's more than one for an email address this will not return accurate counts) then it finds any entries for the same email address with a source of website occurring after that vendor entry and aggregates the totals for that email adding in the vendor entry totals. While it works for the same data provided, it may not work as desired if multiple vendor entries exist for the same email. Without understanding how the totals should occur or if multiple data exists, or understanding why you need this information based on this data, I can't think of a better option without making lots of assumptions.
SELECT A.Email, sum(B.Amount)+sum(A.Amount) as Total_Amount,
count(B.time)+count(A.Time) as Transactions
FROM tableName A
INNER join tableName B
on A.Email=B.Email
AND A.source='vendor'
AND A.Time < B.Time and B.Source='website'
Group By A.Email
This query should give you the desired result by using a subquery to find the persons that have an initial 'vendor' record followed by a 'website' record, before collecting the summary information from the records for these persons.
If you remove the lines marked with -- *, persons whose 'vendor' record is not their first one is also included.
SELECT email, SUM(amount) AS total_amount, COUNT(*) AS transactions
FROM transactions
WHERE email IN
(SELECT t1.email FROM transactions t1
LEFT JOIN transactions t0 -- *
ON t0.email = t1.email AND t0.timestamp < t1.timestamp -- *
LEFT JOIN transactions t2
ON t2.email = t1.email
WHERE t1.source = 'vendor' AND t2.source = 'website'
AND t0.email IS NULL -- *
)
GROUP BY email;
See http://www.sqlfiddle.com/#!2/864898/8/0
Your query should look like this :
select email, sum(amount) ,count(*)
from tbl
where email='daniel#example.com'
group by email;
OR - to count all email !
select email, sum(amount) ,count(*)
from tbl
group by email;
All by vendor
select email, sum(amount) ,count(*)
from tbl
where source ='vendor'
group by email;
Also demo here:
http://sqlfiddle.com/#!2/de36ed/2
Try this :-
select x1.email_id,(x1.tot + x2.tot)as total_amount,(x1.cnt + x2.cnt)as transactions from
(select t1.email_id,count(t1.email_id)as cnt,sum(t1.totalamt)as tot from testdata t1 where t1.sourcee='web' group by t1.email_id)x1
inner join (select t2.email_id,count(t2.email_id)as cnt,sum(t2.totalamt)as tot from testdata t2 where t2.sourcee='vendor' group by t2.email_id)x2
on x1.email_id=x2.email_id group by x1.email_id;
Output :-
Its working fine.If required you please change the field name as per your table structure.
Hope it will help you.

Should I create a new field in the table or just select the MAX of the second table

On this question I received an answer that worked well. I am now wondering if there is a possible better structure.
I have two tables.
Projects : id, title
Status : project_id, status_id, created(DATETIME)
At the moment to get the status of my project, I get the project ID and pull the latest row out of the status table based on the project id. To get this latest row is quite a hassle.
Should I rather change the schema to this?
Projects : id, title, current_status_id(FK)
Status : id(PK), project_id, status_id, created(DATETIME)
Then I can just join the tables with the FK and get the row that I want without looking for the latest?
EDIT:
So I want something like this
SELECT * FROM projects
LEFT JOIN status on projects.id = status.project_id
WHERE projects.id = 1
But I want only the latest record in the status table.
EDIT 2:
So I want something like this
SELECT * FROM projects
LEFT JOIN status on projects.id = status.project_id
But for each project returned, only get the latest status record for that project_id from status.
That's one way to do it.
You might not even need the current_status field to be an FK; why not just store the value? You could get into odd circular references otherwise.
Another way would be to store a status archive, separate of the projects table. Every time the status changes, insert the current status into the archive table, and change the projects.status value.
How is this a hassle?
SELECT project_id, status_id, created
FROM Status
WHERE project_id = the-id
ORDER BY created DESC
LIMIT 1;
Or, if you need a list of multiple projects:
SELECT a.project_id, a.status_id, a.created
FROM Status a
LEFT JOIN Status b
ON a.project_id = b.project_id
AND b.created > a.created
WHERE a.project_id IN(id1, id2, id3) AND b.project_id IS NULL;
So, with project data:
SELECT Projects.*, Status.*
FROM Projects
LEFT JOIN Status
ON Status.project_id = Projects.id
WHERE Projects.id = the-id
ORDER BY Status.created DESC
LIMIT 1;
Or:
SELECT Projects.*, Status.*
FROM Projects
LEFT JOIN Status a
ON a.project_id = Projects.id
LEFT JOIN Status b
ON a.project_id = b.project_id
AND b.created > a.created
WHERE b.project_id IS NULL;

SQL Query to mark entries of n2n relation

Happy last Day of 2011!
I have some troubles finding the right query for my probelm.
I have two tables (mails, customers) which are connected via the third table: mail_has_customers. One can find out, that there are mails, which will be sent to several customers. It's an n2n relation. One mail could have more customers and one customer could have several mails.
I want to check one special mail with the id_mails (PK) = 2. The Result should list all customers and mark those, which are connected to my current mail.
Here are the tables (pseudo)defined:
mails(id, subject, content, PK: id);
customers(id, name, PK: id);
mail_has_customers(id_mails, id_customers, FK: id_mails, id_customers);
My current query looks like following code, but the problem is, I get the customers twice, or more which have more then on mail-connection.
SELECT c.id, c.name, (CASE WHEN hc.id_mails = 2
OR hc.id_id_mails IS NULL
THEN hc.id_mails
ELSE 'x'
END
) AS selected
FROM customers c
LEFT OUTER JOIN mail_has_customers hc ON hc.id_customers = c.id
ORDER BY c.id, selected ASC
I had also tried DISTICT ON, but that somehow didn't work with mysql. Or did I use wrong Syntax? SELECT DISTINCT ON(c.id, c.name) c.id, c.name, (CASE...
UPDATE:
I also tried to GROUP BY c.id that stuff, but then I get the first entries (order by id) which overwrite the later entries of selected and I don't find out if it be possible marked for id_mails = 2.
You shouldn't have to do a distinct or a group by unless there are duplicates in either your customer table or mail_has_customers table; or the relationships are not what you have described.
If you must the syntax is
Select distinct C.ID, C.Name, (CASE WHEN hc.id_mails <> 2
OR hc.id_id_mails IS NULL
THEN hc.id_mails
ELSE 'x'
END) as Selected
However, the logic that's bothering me is the case.
currently you have case when hc.ID mails =2 OR hc.ID_id_Mails is null.
the ID_ID seems incorrect based on your table structure
the logic of looking for 2 and null is perplexing if you just want only the 2's to be selected per stated requirements.
In addition I'm assuming ID is numeric yet your trying to get a result that is numeric and character by casing in an 'X' as selected.
Do you need the HC.Mail_ID in your results? if you do you will have multiple entries for customer thus perhaps the appearance of duplicate customers, though not duplicate records as the hc.id_Mails makes them unique.
So shouldn't the query look something like
SELECT c.id, c.name, (CASE WHEN hc.id_mails = 2 THEN 'x' else null END) AS selected
FROM customers c
LEFT OUTER JOIN mail_has_customers hc ON hc.id_customers = c.id
ORDER BY c.id, selected ASC
Group by C.ID, (CASE WHEN hc.id_mails = 2 THEN 'x' else null END)
The group by only being needed if customer or hc records are duplicated in the base tables.

Rewrite IN subquery as JOIN

I've never had good performance with IN in MySQL and I've hit a performance issue with it again.
I'm trying to create a view. The relevant part of it is:
SELECT
c.customer_id,
....
IF (c.customer_id IN (
SELECT cn.customer_id FROM customer_notes cn
), 1, 0) AS has_notes
FROM customers c;
Basically, I just want to know if the customer has a note attached to it or not. It doesn't matter how many notes. How can I rewrite this using JOIN to speed it up?
The customers table currently has 1.5 million rows so performance is an issue.
Don't you need the customer ID selected? As it stands, aren't you running the subquery once per customer, and getting a stream of true or false values with no idea which one applies to which customer?
If that is what you need, you don't need to reference the customers table (unless you keep your database in a state of semantic disintegrity and there could be entries in customer_notes for which there is no corresponding customer - but then you have bigger problems than the performance of this query); you can simply use:
SELECT DISTINCT Customer_ID
FROM Customer_Notes
ORDER BY Customer_ID;
to obtain the list of customer ID values with at least one entry in the Customer_Notes table.
If you want a list of Customer ID values and an associated true/false value, then you need to do a join:
SELECT C.Customer_ID,
CASE WHEN N.Have_Notes IS NULL THEN 0 ELSE 1 END AS Has_Notes
FROM Customers AS C
LEFT JOIN (SELECT Customer_ID, COUNT(*) AS Have_Notes
FROM Customer_Notes
GROUP BY Customer_ID) AS N
ON C.Customer_ID = N.Customer_ID
ORDER BY C.Customer_ID;
If this gives poor performance, check that you have an index on Customer_Notes.Customer_ID. If that isn't the issue, study the query plan.
Can't do ... in a view
The petty restrictions on what is allowed in a view is always a nuisance in any DBMS (MySQL is not alone in having restrictions). However, we can do it with a single regular join. I just remembered. COUNT(column) only counts non-null values, returning 0 if all values are null, so - if you don't mind getting a count rather than just 0 or 1 - you can use:
SELECT C.Customer_ID,
COUNT(N.Customer_ID) AS Num_Notes
FROM Customers AS C
LEFT JOIN Customer_Notes AS N
ON C.Customer_ID = N.Customer_ID
GROUP BY C.Customer_ID
ORDER BY C.Customer_ID;
And if you absolutely must have 0 or 1:
SELECT C.Customer_ID,
CASE WHEN COUNT(N.Customer_ID) = 0 THEN 0 ELSE 1 END AS Has_Notes
FROM Customers AS C
LEFT JOIN Customer_Notes AS N
ON C.Customer_ID = N.Customer_ID
GROUP BY C.Customer_ID
ORDER BY C.Customer_ID;
Note that the use of 'N.Customer_ID' is crucial - though any column in the table would do (but you've not divulged the names of any other columns, AFAICR) and I'd normally use something other than the joining column for clarity.
I think EXISTS suits your situation better than JOIN or IN.
SELECT
IF (EXISTS (
SELECT *
FROM customer_notes cn
WHERE c.customer_id = cn.customer_id),
1, 0) AS filter_notes
FROM customers
Try this
SELECT
CASE WHEN cn.customer_id IS NOT NULL THEN 1
ELSE 0
END AS filter_notes
FROM customers c LEFT JOIN customer_notes cn
ON c.customer_id= cn.customer_id