SQL query to count non unique field - sql-server

I'm trying to figure out how to add a count on the email field in the query on the end but the problem I have is some of the data required is unique i.e. ID, DateTime but the email is not which I want a count of. I just can't figure it out how to do it in one SQL line.
e.g. Return:-
101, bla, prd, test#test.com, alfred, comp, test, 2015-10-10 10:10:10, 2 <-- count
100, bla, prd, test#test.com, alfred, comp, test, 2015-09-10 10:11:10, 2
099, bla, prd, anoter#email.com, simpson, comp, test, 2014-10-10 10:10:10, 1
098, bla, prd, bla#email.com, henry, comp, test, 2014-05-10 10:10:10, 1
Query
select TOP 200
ID,
FromPage,
Product,
Email,
Name,
Company,
Industry,
DateTime,
(count code here as EmailTotal)
from InstallEmails
WHERE product like 'prd%'
ORDER BY ID DESC

If you are using SQL Server 2005 or later, you can use a window function
SELECT TOP 200
ID,
FromPage,
Product,
Email,
Name,
Company,
Industry,
DateTime,
EmailTotal = COUNT(*) OVER(PARTITION BY Email)
FROM InstallEmails
WHERE product like 'prd%'
ORDER BY ID DESC;
For earlier versions, you will need to use a subquery:
SELECT TOP 200
ID,
FromPage,
Product,
Email,
Name,
Company,
Industry,
DateTime,
EmailTotal = ( SELECT COUNT(*)
FROM ( SELECT TOP 200 Email
FROM InstallEmails
WHERE product like 'prd%'
ORDER BY id DESC
) AS ie2
WHERE ie2.Email = ie.Email
)
FROM InstallEmails AS ie
WHERE product like 'prd%'
ORDER BY ID DESC;

worked it out.. appears to be ok.
select TOP 200 *, (select COUNT(email) from InstallEmails where email = t.email) as EmailTotal
from InstallEmails as t
where product like 'prd%'
ORDER BY ID DESC

Related

Oracle INSERT when selecting from multiple tables

What's wrong with this query? My insert needs to get data from other tables, but when I use select, it gives me error.
Here is the query:
INSERT INTO PAYMENT (
OWNER_HI,
ACCOUNT_ID,
DATE_PAYMENT,
ACCOUNT_VALUE_BEFORE,
CURRENCY,EXCHANGE_RATE,
SUM,
SUM_USD,
DATE_INPUT,
OPERATOR_ID,
DOCUMENT,
INVOICE_ID)
VALUES (
OWNER,
ID,
TODAY,
SALDO,
CURRENCY,
RATE,
50,
(50 * RATE),
TODAY,
386,
'teste sis',
null)
(SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD') "NOW" FROM DUAL) TODAY
(SELECT VALUE FROM ACCOUNT WHERE ACCOUNT_ID = 386) SALDO
(SELECT CURRENCY_IDCURRENCY_ID FROM CURRENCY_EXCHANGE WHERE rownum=1 ORDER BY CURRENCY_ID DESC) CURRENCY
(SELECT EXCHANGE_RATE FROM CURRENCY_EXCHANGE WHERE rownum=1 ORDER BY CURRENCY_ID DESC) RATE;
And this is the erro:
Erro de SQL: ORA-00933: SQL command not properly ended
00933. 00000 - "SQL command not properly ended"
Perhaps what you meant was something more like
INSERT INTO PAYMENT (
OWNER_HI,
ACCOUNT_ID,
DATE_PAYMENT,
ACCOUNT_VALUE_BEFORE,
CURRENCY,EXCHANGE_RATE,
SUM,
SUM_USD,
DATE_INPUT,
OPERATOR_ID,
DOCUMENT,
INVOICE_ID)
VALUES (
OWNER,
ID,
TO_CHAR(SYSDATE, 'YYYY-MM-DD'),
(SELECT VALUE FROM ACCOUNT WHERE ACCOUNT_ID = 386),
(SELECT CURRENCY_IDCURRENCY_ID FROM CURRENCY_EXCHANGE WHERE rownum=1 ORDER BY CURRENCY_ID DESC),
(SELECT EXCHANGE_RATE FROM CURRENCY_EXCHANGE WHERE rownum=1 ORDER BY CURRENCY_ID DESC),
50,
(50 * RATE),
TODAY,
386,
'teste sis',
null);
Best of luck.
We cannot mix INSERT ... VALUES and INSERT ... SELECT syntax. Choose one or the other. As you need values from other tables, you need INSERT ... SELECT.
There is no relationship between the tables you are querying so use a CROSS JOIN. This won't create a problem as long as you select only one row from each.
SELECT EXCHANGE_RATE FROM CURRENCY_EXCHANGE WHERE rownum=1 ORDER BY CURRENCY_ID DESC doesn't do what you think it does, because ROWNUM is allocated before the sort not afterwards. To get the toppermost currency, use an analytic function like ROW_NUMBER() in a sub-query and filter on that.
I've had to make a couple of guesses because you aren't clear about all the business rules you are implementing but you need something like this:
INSERT INTO PAYMENT (
OWNER_HI,
ACCOUNT_ID,
DATE_PAYMENT,
ACCOUNT_VALUE_BEFORE,
CURRENCY,EXCHANGE_RATE,
SUM,
SUM_USD,
DATE_INPUT,
OPERATOR_ID,
DOCUMENT,
INVOICE_ID)
select user, -- where does OWNER come from??
saldo.account_id,
trunc(sysdate),
SALDO.value,
CURRENCY.CURRENCY_ID ,
CURRENCY.EXCHANGE_RATE ,
50,
(50 * CURRENCY.EXCHANGE_RATE ),
trunc(sysdate),
386,
'teste sis',
null
from ( select CURRENCY_ID,
EXCHANGE_RATE,
row_number() over (order by CURRENCY_ID DESC ) as rn
FROM CURRENCY_EXCHANGE ) currency
cross join
(SELECT * FROM ACCOUNT WHERE ACCOUNT_ID = 386) SALDO
where currency.rn = 1
Note: I've ignored your casting of sysdate to a string (as "TODAY") because storing dates as strings is such incredibly bad practice. I'm hoping you're just doing it as a wait of stripping away the time element from sysdate, which we can also achieve with truncation.

Select MAX, but want to show the other columns

Oke, I have this data:
INSERT INTO JobHistory(EmployeeID, EffDate, EffSeq, EmploymentStatus,
JobTitle, Salary, ActionDesc)
VALUES
(1000,'07-31-2008',1,'A','Intern',2000,'New Hire'),
(1000,'05-31-2009',1,'A','Production Technician',2000,'Title Change'),
(1000,'05-31-2009',2,'A','Production Technician',2500,'Salary Change'),
(1000,'11-01-2009',1,'A','Production Technician',3000,'Salary Change'),
(1200,'01-10-2009',1,'A','Design Engineer',5000,'New Hire'),
(1200,'05-01-2009',1,'T','Design Engineer',5000,'Termination'),
(1100,'08-01-2008',1,'A','Accounts Payable Specialist I',2500,'New Hire'),
(1100,'05-03-2009',1,'A','Accounts Payable Specialist II',2500,'Title Change'),
(1100,'05-01-2009',2,'A','Accounts Payable Specialist II',3000,'Salary Change');
and if I do this:
SELECT MAX(EffDate) as MaxDate--, EmployeeID, JobTitle
FROM JobHistory
you will get the MAX EffData(11-01-2009). ok. But now you will see only the column: EffData. But I also want to show the EmployeeID and JobTitle for that only returned record. But you can't do that, because you have to use then GROUP BY. And than you will return more records
ANd with a CTE it is also not possible.
So how to return just one record(the max of Effdata) but also shown the EmployeeID and jobtitle?
Thank you
You need TOP 1 with Order by not Max aggregate
SELECT Top 1 EffDate as MaxDate, EmployeeID, JobTitle
FROM JobHistory
Order by MaxDate desc
In case there is a Tie in max EffDate then and you want to see the Tie records then use TOP 1 with Ties
SELECT Top 1 with Ties EffDate as MaxDate, EmployeeID, JobTitle
FROM JobHistory
Order by MaxDate desc

Identify unique values from 2 rows

There can be multiple account names assigned to 1 account number. Since there are a million of rows in the DB I want to find out how I can query the account # that is only assigned to 1 account. Please see sample data below:
Account # 100, 100, 500, 650, 250, 250, 600, 400, 400
Account Name ABA, DSA, ABA, DSA, ABA, DSA, DSA, ABA, ABA
The result of the query should be account # 500,650,400 because these account # only have one account name assigned to it.
Account 100 and 250 have multiple account names assigned to it. How do I filter account number with only 1 account name assigned to it? Please help
GROUP BY and HAVING is the way to go as stated in a comment above.
select account_num
from your_table
group by account_num
having COUNT(*) = 1
Some people will frown on the * in COUNT(), but if this is an ad hoc, non-production query, I don't think it's a big deal in terms of performance.
Based on the question, I believe the query should return 400,500,600,650.
Using a query with the clause of having count(*) =1 would not return 400 based on the data given in the question.
create table #Accounts (
AccountNumber int not null
, AccountName char(3) not null
)
insert into #Accounts (AccountNumber,AccountName) values
(100,'ABA')
,(100,'DSA')
,(500,'ABA')
,(650,'DSA')
,(250,'ABA')
,(250,'DSA')
,(600,'DSA')
,(400,'ABA')
,(400,'ABA');
with cte as (
select AccountNumber
, AccountName
, RowNumber= row_number() over (
partition by AccountNumber
order by AccountName
)
from #Accounts
group by AccountNumber, AccountName
)
select AccountNumber
, AccountName
from cte
where not exists (
select 1
from cte as i
where i.AccountNumber = cte.AccountNumber
and i.RowNumber = 2
);
This will return the account no and name where the account no is linked to more than one name
NOTE: In your example data, account no 400 is linked to the same name twice...
I'm not sure it that should count as linked to 1 name or not...
This will include AccountNo 400
SELECT DISTINCT AccountNo, AccountName
FROM #Accounts a
WHERE (
SELECT COUNT(DISTINCT AccountName)
FROM #Accounts b
WHERE a.AccountNo = b.AccountNo) = 1
This will NOT include Account No 400 (No DISTINCT in Sub Query)
SELECT DISTINCT AccountNo, AccountName
FROM #Accounts a
WHERE (
SELECT COUNT(AccountName)
FROM #Accounts b
WHERE a.AccountNo = b.AccountNo) = 1

Multiple rows in one

I know there are similar questions like mine but unfortunately I haven't found the corresponding solution to my problem.
First off here's a simplified overview of my tables:
Partner table: PartnerID, Name
Address table: PartnerID, Street, Postcode, City, ValidFrom
Contact table: PartnerID, TypID, TelNr, Email, ValidFrom
A partner can have one or more addresses as well as contact info. With the contact info a partner could have let's say 2 tel numbers, 1 mobile number and 1 email. In the table it would look like this:
PartnerID TypID TelNr Email ValidFrom
1 1 0041 / 044 - 2002020 01.01.2010
1 1 0041 / 044 - 2003030 01.01.2011
1 2 0041 / 079 - 7003030 01.04.2011
1 3 myemail#hotmail.com 01.06.2011
What I need in the end is, combining all tables for each partner, is like this:
PartnerID Name Street Postcode City TelNr Email
1 SomeGuy MostActualStreet MostActualPC MostActualCity MostActual Nr (either type 1 or 2) MostActual Email
Any help?
Here's some T-SQL that gets the answer I think you're looking for if by "Most Actual"
you mean "most recent":
WITH LatestAddress (PartnerID,Street,PostCode,City)
AS (
SELECT PartnerID,Street,PostCode,City
FROM [Address] a
WHERE ValidFrom = (
SELECT MAX(ValidFrom)
FROM [Address] aMax
WHERE aMax.PartnerID = a.PartnerID
)
)
SELECT p.PartnerID,p.Name,la.Street,la.PostCode,la.City
,(SELECT TOP 1 TelNr FROM Contact c WHERE c.PartnerID = p.PartnerID AND TelNr IS NOT NULL ORDER BY ValidFrom DESC) AS MostRecentTelNr
,(SELECT TOP 1 Email FROM Contact c WHERE c.PartnerID = p.PartnerID AND Email IS NOT NULL ORDER BY ValidFrom DESC) AS MostRecentEmail
FROM [Partner] p
LEFT OUTER JOIN LatestAddress la ON p.PartnerID = la.PartnerID
Breaking it down, this example used a common table expression (CTE) to get the latest address information for each Partner
WITH LatestAddress (PartnerID,Street,PostCode,City)
AS (
SELECT PartnerID,Street,PostCode,City
FROM [Address] a
WHERE ValidFrom = (
SELECT MAX(ValidFrom)
FROM [Address] aMax
WHERE aMax.PartnerID = a.PartnerID
)
)
I left-joined from the Partner table to the CTE, because I didn't want partners who don't have addresses to get left out of the results.
FROM [Partner] p
LEFT OUTER JOIN LatestAddress la ON p.PartnerID = la.PartnerID
In the SELECT statement, I selected columns from the Partner table, the CTE, and I wrote two subselects, one for the latest non-null telephone number for each partner, and one for the latest non-null email address for each partner. I was able to do this as a subselect because I knew I was returning a scalar value by selecting the TOP 1.
SELECT p.PartnerID,p.Name,la.Street,la.PostCode,la.City
,(SELECT TOP 1 TelNr FROM Contact c WHERE c.PartnerID = p.PartnerID AND TelNr IS NOT NULL ORDER BY ValidFrom DESC) AS MostRecentTelNr
,(SELECT TOP 1 Email FROM Contact c WHERE c.PartnerID = p.PartnerID AND Email IS NOT NULL ORDER BY ValidFrom DESC) AS MostRecentEmail
I would strongly recommend that you separate your Contact table into separate telephone number and email tables, each with their own ValidDate if you need to be able to keep old phone numbers and email addresses.
Check out my answer on another post which explains how to get the most actual information in a case like yours : Aggregate SQL Function to grab only the first from each group
PS : The DATE_FIELD would be ValidFrom in your case.

SQL - Most recent by date

I have a consumer table which has the columns - Email, AccountState and DateCreated
AccountState can have the values 1 (active), 2 (inactive) and 3 (archived)
A specific consumer can have multiple rows consisting of the account state's above.
What I am trying to do is construct a query which returns the following
A. A list of consumer records for each consumer (using email address)
B. Only the records which aren't the most recent (so if a specific email address has 3 records, 1 for each state, it would return the 2 which aren't the most recent)
Then once I have this list I want to set all these states to 3 as they need to be archived.
So for the example data shown here
Only rows 13 - 16 would be returned.
I have tried to do this using the query below but it isn't working.
SELECT con.Email,
con.Id,
con.DateCreated AS DateRegistered,
con.DateLastActivity,
con.hasiPhone,
con.hasAndroid,
con.hasSMS,
con.CurrencyCode AS Currency,
con.AccountState
FROM Consumer con
WHERE con.AccountState <> 1
AND DateCreated =( SELECT MAX(DateCreated)
FROM Consumer con_most_recent
WHERE con_most_recent.AccountState <> 1
AND con_most_recent.Id = con.Id)
order by Email asc
;WITH x AS
(
SELECT rn = ROW_NUMBER() OVER (PARTITION BY EMail ORDER BY DateCreated DESC),
Email, Id, DateCreated AS DateRegistered --, ... other columns
FROM dbo.Consumer
WHERE AccountState <> 1
)
SELECT Email, Id, DateRegistered --, ... other columns
FROM x
WHERE rn > 1
ORDER BY Email;
EDIT changing state for these rows
;WITH x AS
(
SELECT rn = ROW_NUMBER() OVER (PARTITION BY EMail ORDER BY DateCreated DESC),
Email, Id, DateCreated AS DateRegistered --, ... other columns
FROM dbo.Consumer
WHERE AccountState <> 1
)
UPDATE x
SET AccountState = 3
WHERE rn > 1;

Resources