Identify unique values from 2 rows - sql-server

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

Related

Adding conditions at the WHERE clause gives more results

I use SqlServer. I have a table with lots of columns the importants of which are:
· User_name
· Partition - Date in xxxx-xx-xx format
· Game - a string that works as an ID
· Credits - A number
· Bet - Another number
· Prize - Another number
· Num_Spins - Another number
I wrote a query to select of those the ones that interest me given a specific date.
Select distinct CONCAT(User_Name, DATALENGTH(User_Name)) as User_name, Partition, Game, Bet, Num_spins, Credits, Prize
from ***
where Partition>='2019-09-01' and Partition<'2019-11-17' and Bet>0 and credits is not null
and User_Name IN (Select distinct userName from *** where GeoIpCountryCode='ES')
I wish I could make that a view or something, but unfortunately I don't have the privileges to do so. Therefore, I do a subquery from it:
I want to find out of those rows, the ones whose numbers follow a certain math result: (Credits+Bet-Prize) > 100000 and num_spins>5
Select user_name, partition, count(Game) as difMachines
FROM
(
Select distinct CONCAT(User_Name, DATALENGTH(User_Name)) as User_name, Partition, Game, Bet, Num_spins, Credits, Prize
from ***
where Partition>='2019-09-01' and Partition<'2019-11-17' and Bet>0 and credits is not null
and User_Name IN (Select distinct userName from *** where GeoIpCountryCode='ES')
) as A
where
(Credits+Bet-Prize) > 100000 and num_spins>5
group by User_Name, Partition;
Now, I got all the information I need. I run the last query, to group_by date these results so I can analyze them:
Select datepart(week,Partition) as Week, count (distinct user_name) as Users
from (
Select user_name, partition, count(Game) as difMachines
FROM
(
Select distinct CONCAT(User_Name, DATALENGTH(User_Name)) as User_name, Partition, Game, Bet, Num_spins, Credits, Prize
from ***
where Partition>='2019-09-01' and Partition<'2019-11-17' and Bet>0 and credits is not null
and User_Name IN (Select distinct userName from *** where GeoIpCountryCode='ES')
) as A
where
(Credits+Bet-Prize) > 100000 and num_spins>5
group by User_Name, Partition
) as B
Where difMachines=1
group by datepart(week,Partition)
order by Week asc;
I know the query can be optimized, but that's not what troubles me. The problem is that when running this query, I obtain at week 36 17050 users. If I change this line (Credits+Bet-Prize) > 100000 and num_spins>5 for this one (Credits+Bet-Prize) > 100000 (so, I purely remove the num_spins>5 part), I get 16800 users instead.
To sum up, I get more results by being more restrictive in my query. That does not make sense to me. Someone please can help? Head me to the right direction or something?
Thank you
You are trying to get the count of result set with this filter diffmachine=1,isn't?. but if you remove the filter num_spins>5 then count will increase for diffmachine greater than 1.here i give an example like yours
Declare #t table
(
[user_name] varchar(5), [partition] date, Game varchar(10),num_spins int
)
insert into #t
select 'a','01nov19','g1',1
union all
select 'a','01nov19','g1',2
union all
select 'a','01nov19','g1',3
union all
select 'a','01nov19','g1',4
union all
select 'a','01nov19','g1',5
union all
select 'a','01nov19','g1',6
union all
select 'b','01nov19','g1',7
select * from
(
select [user_name],[partition],count(game) cnt
from #t
where num_spins>5
group by [user_name],[partition]
)a
where cnt=1

How to skip the max function which has only one entry when i do a group by in SQL Server

I have a requirement where I do a group by the table
Table
Name salary
------------
abc 10000
abc 1000
def 100
Query:
select max(salary)
from table
group by Name
Result:
abc 10000
def 100
I don't want 'def' to be displayed since it's a single entry in the table. How can I achieve this?
You can add a HAVING clause.
Having specifies a search condition for a group or an aggregate.
HAVING can be used only with the SELECT statement. HAVING is typically
used with a GROUP BY clause. When GROUP BY is not used, there is an
implicit single, aggregated group.
select
Name
,max(salary)
from table
group by Name having count(*) > 1
This will only return the aggregates for names that have more than 1 row, which seems to be what you want.
EXAMPLE
declare #table table (name varchar(16), salary int)
insert into #table
values
('abc',10000),
('abc',1000),
('def',100),
('xxf',100)
select
Name
,max(salary)
from #table
group by Name
having count(*) > 1

Get total sum for in Sql server for unknown location?

There is an insurance policy and this policy can be paid by 1-3 agents.
line #1 ) for example : for policy Id 1 , an agent who's ID is 100 , paid 123
line #3 ) for example : for policy Id 3 , an agent who's ID is 999 , paid 741 , and also another agent who's ID is 100 paid 874
(the representation is not how it should be done correctly , but that how I have it as a fact).
How can I found how much agent ID 100 has paid total ?
(123+541+874+557+471+552)
I have a very ugly union's solution.
SQL ONLINE
In a well normalized model this is a simple query. You can 'normalize' in a CTE query then sum:
with cte as (
select agent1id as id, agent1sum as s
from insurance where agent1id is not null
union all
select agent2id as id, agent2sum as s
from insurance where agent2id is not null
union all
select agent3id as id, agent3sum as s
from insurance where agent3id is not null
)
select sum( s)
from cte
where id = 100
This is a friendly index approax if your table contains index for agents columns. A friendly index query avoid full table scan.
Looks like
SUM(
CASE WHEN agent1id=100 THEN agent1sum ELSE 0 END +
CASE WHEN agent2id=100 THEN agent2sum ELSE 0 END +
CASE WHEN agent3id=100 THEN agent3sum ELSE 0 END)
should aggregate it properly. If you need to do it for all agents, I'd use the agent table or use a CTE before this query to get the distinct agent IDs, then replace 100 above.

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.

Problem with unique SQL query

I want to select all records, but have the query only return a single record per Product Name. My table looks similar to:
SellId ProductName Comment
1 Cake dasd
2 Cake dasdasd
3 Bread dasdasdd
where the Product Name is not unique. I want the query to return a single record per ProductName with results like:
SellId ProductName Comment
1 Cake dasd
3 Bread dasdasdd
I have tried this query,
Select distict ProductName,Comment ,SellId from TBL#Sells
but it is returning multiple records with the same ProductName. My table is not realy as simple as this, this is just a sample. What is the solution? Is it clear?
Select ProductName,
min(Comment) , min(SellId) from TBL#Sells
group by ProductName
If y ou only want one record per productname, you ofcourse have to choose what value you want for the other fields.
If you aggregate (using group by) you can choose an aggregate function,
htat's a function that takes a list of values and return only one : here I have chosen MIN : that is the smallest walue for each field.
NOTE : comment and sellid can come from different records, since MIN is taken...
Othter aggregates you might find useful :
FIRST : first record encountered
LAST : last record encoutered
AVG : average
COUNT : number of records
first/last have the advantage that all fields are from the same record.
SELECT S.ProductName, S.Comment, S.SellId
FROM
Sells S
JOIN (SELECT MAX(SellId)
FROM Sells
GROUP BY ProductName) AS TopSell ON TopSell.SellId = S.SellId
This will get the latest comment as your selected comment assuming that SellId is an auto-incremented identity that goes up.
I know, you've got an answer already, I'd like to offer a way that was fastest in terms of performance for me, in a similar situation. I'm assuming that SellId is Primary Key and identity. You'd want an index on ProductName for best performance.
select
Sells.*
from
(
select
distinct ProductName
from
Sells
) x
join
Sells
on
Sells.ProductName = x.ProductName
and Sells.SellId =
(
select
top 1 s2.SellId
from
Sells s2
where
x.ProductName = s2.ProductName
Order By SellId
)
A slower method, (but still better than Group By and MIN on a long char column) is this:
select
*
from
(
select
*,ROW_NUMBER() over (PARTITION BY ProductName order by SellId) OccurenceId
from sells
) x
where
OccurenceId = 1
An advantage of this one is that it's much easier to read.
create table Sale
(
SaleId int not null
constraint PK_Sale primary key,
ProductName varchar(100) not null,
Comment varchar(100) not null
)
insert Sale
values
(1, 'Cake', 'dasd'),
(2, 'Cake', 'dasdasd'),
(3, 'Bread', 'dasdasdd')
-- Option #1 with over()
select *
from Sale
where SaleId in
(
select SaleId
from
(
select SaleId, row_number() over(partition by ProductName order by SaleId) RowNumber
from Sale
) tt
where RowNumber = 1
)
order by SaleId
-- Option #2
select *
from Sale
where SaleId in
(
select min(SaleId)
from Sale
group by ProductName
)
order by SaleId
drop table Sale

Resources