SQL query - Selecting distinct values from a table - sql

I have a table in which i have multiple entries against a FK. I want to find out the value of FK which do not have certain entries e.g
my table has following entries.
PK----------------FK-----------------Column entries
1----------------100-----------------ab1
2----------------100-----------------ab2
3----------------100-----------------ab4
4----------------200-----------------ab1
5----------------200-----------------ab2
6----------------200-----------------ab3
7----------------300-----------------ab1
8----------------300-----------------ab2
9----------------300-----------------ab3
10---------------300-----------------ab4
Now, from this table i want to filter all those FK which do not have ab3 or ab4 in them. Certainly, i expect distinct values i.e. in this case result would be FK= 100 and 200.
The query which i am using is
select distinct(FK)
from table1
where column_entries != 'ab3'
or column_entries != 'ab4';
Certainly, this query is not fetching the desired result.

try the following :-
select distinct fk_col from table1
minus
(select distinct fk_col from table1 where col_entry='ab3'
intersect
select distinct fk_col from table1 where col_entry='ab4')
This will show all those FKs which do not have 'ab3' and 'ab4'. i.e. 100 and 200 in your case

The below script may be the solution if I got your question in a right way.
SELECT DISTINCT(TableForeignKey)
FROM Test
WHERE TableForeignKey NOT IN (
SELECT T1.TableForeignKey
FROM Test T1 INNER JOIN Test T2 ON T1.TableForeignKey = T2.TableForeignKey
WHERE T1.TableEntry = 'ab3' AND T2.TableEntry = 'ab4')
SQLFiddle Demo

You could use GROUP BY with conditional aggregation in HAVING:
SELECT FK
FROM table1
GROUP BY FK
HAVING COUNT(CASE column_entries WHEN 'ab3' THEN 1 END) = 0
OR COUNT(CASE column_entries WHEN 'ab4' THEN 1 END) = 0
;
The two conditional aggregates count 'ab3' and 'ab4' entries separately. If both end up with results greater than 0, then the corresponding FK has both 'ab3' and 'ab4' and is thus not returned. If at least one of the counts evaluates to 0, then FK is considered satisfying the requirements.

Related

SQL select either table 1 or table 2

I have two following queries. How can I modify this into single query? The result should be true if either query returns data, and false otherwise:
select custId from customer where customerId=3425632456 and custPhone='5653663251';
select accountnumber from account where accountId=524526 and accountPhone='5653663251';
Here custPhone=accountPhone
I think that you want exists:
select case
when exists (select custId from customer where customerId=3425632456 and custPhone='5653663251')
then 1
when exists (select accountnumber from account where accountId=524526 and accountPhone='5653663251')
then 1
else 0
end res
from dual
This query always return a single row, with a single column called res. If any of the subqueries returns something, then res has value 1, else 0.
As a performance bonus for using case, the second subquery is not executed if the first succeeds (this is called short-circuit evaluation). If your queries are time consuming, this can be interesting; make sure to put the less expensive query first.
If you actually want to return the values, then that's different. One option is union all:
select custId from customer where customerId=3425632456 and custPhone='5653663251'
union all
select accountnumber from account where accountId=524526 and accountPhone='5653663251'
Note that, unlike the first query, this does not guarantee that only one row will be returned. Depending on your data, this could give any number of row, 0 included. You might need additional casting to align the datatypes.
You could do a FULL OUTER JOIN on the two tables and check for the count using CASE statement:
SELECT
CASE
WHEN COUNT(*) > 0
THEN 'TRUE'
ELSE 'FALSE'
END result,
FROM
customer c
FULL OUTER JOIN
account a
ON c.custPhone = a.accountPhone
WHERE c.customerId=3425632456
AND a.accountId=524526
AND c.custPhone='5653663251;
Try this
select
custId,
accountnumber
from customer c
left join account a
on c.custPhone = a.accountPhone
where customerId = 3425632456
;

using correlated subquery in the case statement

I’m trying to use a correlated subquery in my sql code and I can't wrap my head around what I'm doing wrong. A brief description about the code and what I'm trying to do:
The code consists of a big query (ALIASED AS A) which result set looks like a list of customer IDs, offer IDs and response status name ("SOLD","SELLING","IRRELEVANT","NO ANSWER" etc.) of each customer to each offer. The customers IDs and the responses in the result set are non-unique, since more than one offer can be made to each customer, and a customer can have different response for different offers.
The goal is to generate a list of distinct customer IDs and to mark each ID with 0 or 1 flag :
if the ID has AT LEAST ONE offer with status name is "SOLD" or "SELLING" the flag should be 1 otherwise 0. Since each customer has an array of different responses, what I'm trying to do is to check if "SOLD" or "SELLING" appears in this array for each customer ID, using correlated subquery in the case statement and aliasing the big underlying query named A with A1 this time:
select distinct
A.customer_ID,
case when 'SOLD' in (select distinct A1.response from A as A1
where A.customer_ID = A1.customer_ID) OR
'SELLING' in (select distinct A1.response from A as A1
where A.customer_ID = A1.customer_ID)
then 1 else 0 end as FLAG
FROM
(select …) A
What I get is a mistake alert saying there is no such object as A or A1.
Thanks in advance for the help!
You can use exists with cte :
with cte as (
<query here>
)
select c.*,
(case when exists (select 1
from cte c1
where c1.customer_ID = c.customer_ID and
c1.response in ('sold', 'selling')
)
then 1 else 0
end) as flag
from cte c;
You can also do aggregation :
select customer_id,
max(case when a.response in ('sold', 'selling') then 1 else 0 end) as flag
from < query here > a;
group by customer_id;
With statement as suggested by Yogesh is a good option. If you have any performance issues with "WITH" statement. you can create a volatile table and use columns from volatile table in your select statement .
create voltaile table as (select response from where response in ('SOLD','SELLING').
SELECT from customer table < and join voltaile table>.
The only disadvantge here is volatile tables cannot be accessed after you disconnect from session.

Select records where the only exist 1 in a joined table

I have the following query:
SELECT
A.POSTCARD_ID, A.STAMP_ID, B.END_DT
FROM
PST_VS_STAMP A
JOIN
STAMP B ON A.POSTCARD_ID = B.POSTCARD_ID
WHERE
B.ACCOUNT LIKE 'AA%'
AND B.END_DT = '9999-12-31'
GROUP BY
A.POSTCARD_ID, A.STAMP_ID, B.END_DT
HAVING
COUNT(A.POSTCARD_ID) < 2
But I get the wrong results.
I want only the postcards ID's where there is 1 record (HAVING < 2) in the PST_VS_STAMP table. How can I query this?
Do the aggregation in the subquery, only on the table where you want one row. Because there is one row, you can use an aggregation function to pull out the value of any column (for one row min(col) is the column's value):
select s.postcard_id, vs.stamp_id, s.end_dt
from stamp s join
(select vs.postcard_id, min(stamp_id) as stamp_id
from pst_vs_stamp vs
group by vs.postcard_id
having count(*) = 1
) s
on vs.POSTCARD_ID = s.POSTCARD_ID
where s.ACCOUNT like 'AA%' and s.END_DT = '9999-12-31';

How to search for matching staff number in sql

I am new to sql and trying to come up with a sql query which will list me the duplicate staff which were created in our system.
We have one staff which is created with id as 1234 and the same user has another account starting with staff id 01234. Is there anyway i can get the matching staff
Once i come up with correct duplicates i will than want to delete the accounts which don't have "0" at the start e.g deleted 1234 and only keep 01234
below is the sql
SELECT tps_user.tps_title AS [Name] , tps_user_type.tps_title AS [User Type]
FROM tps_user INNER JOIN
tps_user_type ON tps_user.tps_user_type_guid = tps_user_type.tps_guid
WHERE (tps_user.tps_title IN
(SELECT tps_title AS users
FROM tps_user AS t1
WHERE (tps_deleted = 0)
GROUP BY tps_title
HAVING (COUNT(tps_title) > 1))) AND (tps_user.tps_deleted = 0)
When you do you select try this:
SELECT DISTINCT CONVERT(INT,ID)
FROM your_table
WHERE ...
OR
SELECT ID
FROM your_table
WHERE ...
GROUP BY ID
This will convert all the id's to an int temporarily so when the distinct evaluates duplicates everything will be uniform to give you an accurate representation of the duplicates.
IF you don't want to convert them maybe convert them and insert them into a temporary table and add a flag to which ones have a leading zero. Or convert them then append a zero after you delete the duplicates since you want that anyway. It is easy to append a 0.
the below query will give you the list of duplicates with same Name and title. -
SELECT tps_user.tps_title AS [Name] ,
tps_user_type.tps_title AS [UserType],
COUNT(*) Duplicate_Count
FROM tps_user
INNER JOIN tps_user_type
ON tps_user.tps_user_type_guid = tps_user_type.tps_guid
group by tps_user.tps_title, tps_user_type.tps_title
having COUNT(*) > 1
order by Duplicate_Count desc
Select t1.stringId
from mytable t1
inner join mytable t2 on Convert(INT, t1.intId) = CONVERT(INT, t2.intId)
where t1.stringId not like '0%'
This should list all the persons that have duplicates but do not start with 0.

Counting multiple columns with the same criteria

I would like to count multiple columns based on their contents. Each column I want to count(alpha,bravo and charlie) has either 'T' or 'F', I only want to count those with a 'T'.
So far i have:
select t1.name, count(t1.alpha), count(t1.bravo), count(t1.charlie)
from t1
where alpha='T',
or bravo='t'
or charlie='t'
group by name
order by name asc;
However the figures i am getting dont match what I get if i run
select count(*)
from t1
where name='test'
and alpha='t';
this example would need to be run in 16 different configurations to get the results i am seeking hence it not being a practical solution to write multiple versions of it.
Remove the where clause and use Conditional Aggregate instead, which will help you to count the rows only when data is T.
select t1.name,
count(case when t1.alpha ='T' then 1 end),
count(case when t1.bravo='T' then 1 end),
count(case when t1.charlie='T' then 1 end)
from t1
group by name
order by name asc;