Check if an account# exist in another database - sql

Consider the following
-- Get all objects from database A
use database_a;
select o.objectnumber
into #temp
from EDDSDBO.objects o
group by d.objectnumber;
-- #temp holds 0001, 0002
-- Get all objects from database B
use database_b;
select o.objectnumber,
case when o.objectnumer in #temp then 1 else 0 end as Match
from EDDSDBO.objects o
group by o.objectnumber;
-- Expected output
objectnumber Match
0001 1
0002 1
0003 0
But I get an error: incorrect syntax near objectnumber.
I cannot seem to get this query right. What is the right syntax here?
Any help is greatly appreciated :-)
P.S. I'm on SQL Server 2008

If objectnumber is unique in each table, then you can just use LEFT JOIN with a 3 part object name:
SELECT b.objectnumber,
Match = CASE WHEN a.objectnumber IS NOT NULL THEN 1 ELSE 0 END
FROM database_b.EDDSDBO.objects b
LEFT JOIN database_a.EDDSDBO.objects a
ON a.objectnumber = b.objectnumber
If it is not unique, you can still do this, but you will need to use group by and an aggregate:
SELECT b.objectnumber,
Match = MAX(CASE WHEN a.objectnumber IS NOT NULL THEN 1 ELSE 0 END)
FROM database_b.EDDSDBO.objects b
LEFT JOIN database_a.EDDSDBO.objects a
ON a.objectnumber = b.objectnumber
GROUP BY b.objectnumber;
The important part is there is no need to use a temporary table, this is unnecessary overhead on tempdb, and you also lose the use of any index on objectnumber.

Change the second part of your query to
use database_b;
select o.objectnumber,
case when o.objectnumer in (select distinct objectnumber from #temp) then 1
else 0 end as Match
from EDDSDBO.objects o
group by o.objectnumber;

You shouldn't need to use the GROUP BY clause for this example.
The match column is used to show whether a row exists in tableB for some value of objectnumber in tableA. If you only want to indicate the existence of a row in tableB, and not show the number of rows, then you do not need to use the GROUP BY clause.
The first step should be to create a new set that contains only the set of rows from tableA that you want to compare against tableB. You can then create a sub-query to indicate whether tableA.objectnumber exists in tableB, using the NOT EXISTS operator.
With SubsetA(objectnumber) as (
select distinct objectnumber
from tableA)
select sa.objectnumber,
(case when exists (select null from tableB tb where sa.objectnumber = tb.objectnumber) then 1 else 0 end) as Match
from SubsetA sa

Related

Find value that can be 0 or can be greater than 0 but must have at least one record equal 0

I need to find records that equal 0 and have other records that are greater than 0. The result should have at least one record that equals 0 and must also have at least one record that is greater than 0. It is easier to explain it with visuals:
Name amount
a1 0
a1 100
a1 200
a2 0
a2 0
a2 200
a3 200
a3 0
a3 100
It should not look like:
Name amount
a5 100
a5 100
a5 200
a7 0
a7 0
a7 0
a6 200
a6 10
a6 100
I have tried this:
Select name, amount
from table1
where amount = '0' AND amount > '0'
Sorry if this question is a bit ambiguous, it's rather hard to explain.
Thanks in advance.
NB - Sorry if the question is not clear enough, wasn't sure how to word it.
SELECT will give you rows from the table and the WHERE applies to those rowsto filter them. So your sample:
Select name, amount from table1 where amount = '0' AND amount > '0'
Will never return any rows because it only returns rows that have both amount = 0 and amount > 0 which is impossible. Also I hope those values are numeric, so you shouldn't use the single quotes around them (i.e. '0' should be plain 0)
GMB has a good way to do it with partition functions. The subquery reshapes the data into a new resultset that contains new columns 'min_amount' and 'max_amount' for all rows with the same id along with the other data for each row. You can then filter on those values, although you don't mention if negative values could be present.
Another way to do it would be to add the checks to your filter criteria:
select name, amount
from table1 a
where a.id in (select id from table1 where amount = 0)
and a.id in (select id from table1 where amount > 0)
This selects rows where id is in the list of ids with 0 as amount and the list of ids with amounts > 0.
You can use window functions. Assuming that there are no negative values, you can do:
select name, amount
from (
select
t.*,
min(amount) over(partition by name) min_amount,
max(amount) over(partition by name) max_amount
from mytable t
) t
where min_amount = 0 and max_amount > 0
If there are negative values:
select name, amount
from (
select
t.*,
max(case when amount = 0 then 1 end) over(partition by name) has_zero_amount,
max(amount) over(partition by name) max_amount
from mytable t
) t
where has_zero_amount = 1 and max_amount > 0
I would expect that window functions would be more efficient that other solutions including several subqueries.
And since there are usually multiple ways to solve any given problem, here's another one.
The question you have is a pretty normal pattern. Select a data set that meets N conditions. In this case, there are just two, and both require that a certain type of row exists for given values. So another way to get there is to use the EXISTS clause.
It's logically very similar to Jason's method using IN clauses. Jason's answer, by the way, is deserving of being selected as THE answer. I'm just offering this up as an alternative approach to help you keep your options open.
An EXISTS clause uses a "correlated subquery", which means that a value from the outer query, table1 as t1, is used by the inner queries (inside the parenthesis, if that helps you picture it), usually in the WHERE clause of the inner query.
So below, each of the EXISTS clauses looks for any occurrence of the conditions in their WHERE clauses; the names match and the amounts meet their criteria. If any occurrence is found, the query returns a result set, which the EXISTS interprets as TRUE. If not, nothing comes back, which the EXISTS interprets as FALSE. Note that the results of the inner query don't matter at all, just that the WHERE clause is satisfied, or not. So I use SELECT 1 to show that the return value doesn't matter. You can put anything you want there, actually; even 1/0, and it'll work just fine.
select
*
from
table1 as t1
where
exists (select 1
from table1 as t2
where t2.amount = 0
and t2.Name = t1.Name)
and
exists (select 1
from table1 as t3
where t3.amount > 0
and t3.Name = t1.Name);
See it in action here: Rextester demo
I would phrase this as:
Select t1.name, t1.amount
from table1 t1
where (t1.amount = 0 and
exists (select 1 from table1 tt1 where tt1.name = t1.name and tt1.amount > 0
) or
(t1.amount > 0 and
exists (select 1 from table1 tt1 where tt1.name = t1.name and tt1.amount = 0
) ;
This can take advantage of an index on table1(name, amount). And the beauty of the additional comparison is that only one exists clause needs to be evaluated for each row in the original table.

SQL Nested Select Statement

I have the following SQL Code which is not giving me my desired results.
SELECT
POLICIES.CLIENTS_ID,
POLICIES.CLIENTCODE,
COUNT(POLICIES.POLICIES_ID) as [Total Policies],
(
SELECT
COUNT(POLICIES.POLICIES_ID)
FROM
POLICIES
WHERE
POLICIES.COVCODE = 'AUT'
) as [Auto Policies]
FROM
POLICIES
LEFT JOIN CLIENTS
ON CLIENTS.CLIENTS_ID = POLICIES.CLIENTS_ID
WHERE
POLICIES.CNR IS NULL
GROUP BY
POLICIES.CLIENTS_ID,
POLICIES.CLIENTCODE
ORDER BY
POLICIES.CLIENTS_ID
I get a result like this:
ID CODE Total Auto
3 ABCDE1 1 999999
4 ABCDE2 1 999999
5 ABCDE3 2 999999
6 ABCDE4 2 999999
I would like for the last column to COUNT the total auto policies that exists for that clientid rather than all of the auto policies that exist. I believe I need a nested select statement that somehow groups all like results on the clientid, but it ends up returning more than 1 row and throws the error.
If I add:
GROUP BY
POLICIES.CLIENTS_ID
I get:
Subquery returned more than 1 value. This is not permitted when the....
Any help would be appreciated greatly!
Thank you
You can use a CASE statement to do this. Instead of your subquery in the SELECT clause use:
SUM(CASE WHEN POLICIES.COVCODE = 'AUT' THEN 1 ELSE 0 END) as [AUTO POLICIES]
As Martin Smith pointed out. If client_id has multiple client_codes then this will give you the count of records for each combination of client_id/client_code. If client_id is 1:1 with client_code then this will give you a count of records for each distinct client_id, which I suspect is the case from your example and question.
Unrelated: You have a LEFT JOIN to your Clients table, but you don't use your Clients table anywhere int he query. Consider removing it if you don't need to select or filter by any its fields, since it's just unused overhead.
What if you modify the inner query for getting count to something like
SUM(CASE WHEN POLICIES.COVCODE = 'AUT' THEN 1 ELSE 0 END) as [Auto Policies]

Update with results of another sql

With the sql below I count how many records I have in tableB for each code. The total field is assigned the result of the count and the code the code field of the record.
SELECT
"count" (*) as total,
tableB."code" as code
FROM
tableB
WHERE
tableB.code LIKE '%1'
GROUP BY
tableB.code
In tableA I have a sequence field and I update with the result of total (obtained in the previous sql) plus 1 Do this for each code.
I tried this and it did not work, can someone help me?
UPDATE tableA
SET tableA.sequence = (tableB.total + 1) where tableA."code" = tableB.code
FROM
(
SELECT
"count" (*) as total,
tableB."code" as code
FROM
tableB
WHERE
tableB.code LIKE '%1'
GROUP BY
tableB.code
)
I edited for my tables are as mostar believe facillita understanding of my need
tableA
code sequence
100 null
200 null
table B
code sequence
100 1
100 2
100 3
100 4
......
100 17
200 1
200 2
200 3
200 4
......
200 23
Need to update the sequence blank field in tableA with the number 18 to code = 100
Need to update the sequence blank field in tableA with the number 24 to code = 200
This assumes that code is unique in table_a:
with max_seq as (
select code,
max(sequence) + 1 as max_seq
from table_b
group by code
)
update table_a
set sequence = ms.max_seq
from max_seq ms
where table_a.code = ms.code;
SQLFiddle example: http://sqlfiddle.com/#!15/745a7/1
UPDATE tbl_a a
SET sequence = b.next_seq
FROM (
SELECT code, max(sequence) + 1 AS next_seq
FROM tbl_b
GROUP BY code
) b
WHERE a.code = b.code;
SQL Fiddle.
Only columns of the target table can be updated. It would not make sense to table-qualify those. Consequently, this is not allowed.
Every subquery must have a table alias for the derived table.
I would not use a CTE for a simple UPDATE like this. A subquery in the FROM clause is typically simpler and faster.
No point in double-quoting the aggregate function count(). No pint in double-quoting perfectly legal, lower case identifiers, either. No point in table-qualifying columns in a subquery on a single table in a plain SQL command (no harm either).
You don't need a WHERE condition, since you want to UPDATE all rows (as it seems). Note that only row with matching code are updated. Other rows in tbl_b remain untouched.
Basically you need to read the manual about UPDATE before you try any of this.

duplicate deletion problem

I am working on a database where i have two tables.
BILL_MASTER (master_table)
Bill_Master_ID
Consumer_No
BILL_GENERATION (Detail table)
Bill_Master_ID
Somehow user has inserted two identical consumer #'s in bill master but bill_master_id is different..here is the example of bill_master table
Bill_Master_ID Consumer_No
1 1234567890
2 1234567890
now user has made one transaction of bill_master_id "1" and record exists in Bill_Generation table.
What i want to do is when i pass consumer # in SQL statement as parameter it check if selected consumer# Bill_master_id exist in bill_generation or not. If yes then count return should be 1 else 0.
If I understand you correctly, this should do what you want.
IF (SELECT COUNT(consumer_no) FROM bill_master m
inner join bill_generation g on m.bill_master_id=g.bill_master_id
WHERE consumer_no=1234567890
) > 1
SELECT 1
ELSE
SELECT 0
The title is misleading, this has nothing to do with deletion unless that's additional to the question.
So what you mean is, based on consumer no, you need to find any record in bill_generation for any of the consumer's ids in bill_master?
select case when exists (
select *
from bill_master m
inner join bill_generation g on g.bill_master_id = m.bill_master_id
where m.consumer_no = 1234 -- or some number
) then 1 else 0 end
To actually delete records from bill_master except the lowest id, you can use this
;with tmp as (
select *, rn = row_number() over (partition by consumer_no order by bill_master_id)
from bill_master)
delete tmp where rn > 1

Select values in SQL that do not have other corresponding values except those that i search for

I have a table in my database:
Name | Element
1 2
1 3
4 2
4 3
4 5
I need to make a query that for a number of arguments will select the value of Name that has on the right side these and only these values.
E.g.:
arguments are 2 and 3, the query should return only 1 and not 4 (because 4 also has 5). For arguments 2,3,5 it should return 4.
My query looks like this:
SELECT name FROM aggregations WHERE (element=2 and name in (select name from aggregations where element=3))
What do i have to add to this query to make it not return 4?
A simple way to do it:
SELECT name
FROM aggregations
WHERE element IN (2,3)
GROUP BY name
HAVING COUNT(element) = 2
If you want to add more, you'll need to change both the IN (2,3) part and the HAVING part:
SELECT name
FROM aggregations
WHERE element IN (2,3,5)
GROUP BY name
HAVING COUNT(element) = 3
A more robust way would be to check for everything that isn't not in your set:
SELECT name
FROM aggregations
WHERE NOT EXISTS (
SELECT DISTINCT a.element
FROM aggregations a
WHERE a.element NOT IN (2,3,5)
AND a.name = aggregations.name
)
GROUP BY name
HAVING COUNT(element) = 3
It's not very efficient, though.
Create a temporary table, fill it with your values and query like this:
SELECT name
FROM (
SELECT DISTINCT name
FROM aggregations
) n
WHERE NOT EXISTS
(
SELECT 1
FROM (
SELECT element
FROM aggregations aii
WHERE aii.name = n.name
) ai
FULL OUTER JOIN
temptable tt
ON tt.element = ai.element
WHERE ai.element IS NULL OR tt.element IS NULL
)
This is more efficient than using COUNT(*), since it will stop checking a name as soon as it finds the first row that doesn't have a match (either in aggregations or in temptable)
This isn't tested, but usually I would do this with a query in my where clause for a small amount of data. Note that this is not efficient for large record counts.
SELECT ag1.Name FROM aggregations ag1
WHERE ag1.Element IN (2,3)
AND 0 = (select COUNT(ag2.Name)
FROM aggregatsions ag2
WHERE ag1.Name = ag2.Name
AND ag2.Element NOT IN (2,3)
)
GROUP BY ag1.name;
This says "Give me all of the names that have the elements I want, but have no records with elements I don't want"