Update with results of another sql - 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.

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 - Count Results of 2 Columns

I have the following table which contains ID's and UserId's.
ID UserID
1111 11
1111 300
1111 51
1122 11
1122 22
1122 3333
1122 45
I'm trying to count the distinct number of 'IDs' so that I get a total, but I also need to get a total of ID's that have also seen the that particular ID as well... To get the ID's, I've had to perform a subquery within another table to get ID's, I then pass this into the main query... Now I just want the results to be displayed as follows.
So I get a Total No for ID and a Total Number for Users ID - Also would like to add another column to get average as well for each ID
TotalID Total_UserID Average
2 7 3.5
If Possible I would also like to get an average as well, but not sure how to calculate that. So I would need to count all the 'UserID's for an ID add them altogether and then find the AVG. (Any Advice on that caluclation would be appreciated.)
Current Query.
SELECT DISTINCT(a.ID)
,COUNT(b.UserID)
FROM a
INNER JOIN b ON someID = someID
WHERE a.ID IN ( SELECT ID FROM c WHERE GROUPID = 9999)
GROUP BY a.ID
Which then Lists all the IDs and COUNT's all the USERID.. I would like a total of both columns. I've tried warpping the query in a
SELECT COUNT(*) FROM (
but this only counts the ID's which is great, but how do I count the USERID column as well
You seem to want this:
SELECT COUNT(DISTINCT a.ID), COUNT(b.UserID),
COUNT(b.UserID) * 1.0 / COUNT(DISTINCT a.ID)
FROM a INNER JOIN
b
ON someID = someID
WHERE a.ID IN ( SELECT ID FROM c WHERE GROUPID = 9999);
Note: DISTINCT is not a function. It applies to the whole row, so it is misleading to put an expression in parentheses after it.
Also, the GROUP BY is unnecessary.
The 1.0 is because SQL Server does integer arithmetic and this is a simple way to convert a number to a decimal form.
You can use
SELECT COUNT(DISTINCT a.ID) ...
to count all distinct values
Read details here
I believe you want this:
select TotalID,
Total_UserID,
sum(Total_UserID+TotalID) as Total,
Total_UserID/TotalID as Average
from (
SELECT (DISTINCT a.ID) as TotalID
,COUNT(b.UserID) as Total_UserID
FROM a
INNER JOIN b ON someID = someID
WHERE a.ID IN ( SELECT ID FROM c WHERE GROUPID = 9999)
) x

Simple WHERE clause but keep extracted rows and fill them will null values

I have a table which basically looks like this one:
Date | Criteria
12-04-2016 123
12-05-2016 1234
...
Now I want to select those rows with values in the column 'Criteria' within a given range but I want to keep the extracted rows. The extracted rows should get the value 'null' for the column 'Criteria'. So for example, if I want to select the row with 'Criteria = 123' my result should look like this:
Date | Criteria
12-04-2016 123
12-05-2016 null
Currently I am using this query to get the result:
SELECT b.date, a.criteria
FROM (SELECT id, date, criteria FROM ABC WHERE criteria > 100 and criteria < 200) a
FULL OUTER JOIN ABC b ON a.id = b.id ORDER BY a.criteria
Someone told me that full outer joins perform very badly. Plus my table has like 400000 records and the query is used pretty often. So anyone has an idea to speed up my query? Btw I am using the Oracle11g database.
Do you just want a case expression?
SELECT date,
(case when criteria > 100 and criteria < 200 then criteria end) as criteria
FROM ABC;

SQL simplifying an except query

I have a database with around 50 million entries showing the status of a device for a given day, simplified to the form:
id | status
-------------
1 | Off
1 | Off
1 | On
2 | Off
2 | Off
3 | Off
3 | Off
3 | On
...
such that each id is guaranteed to have at least 2 rows with an 'off' status, but doesn't have to have an 'on' status. I'm trying to get a list of only the ids that do not have an 'On' status. For example, in the above data set I'd want a query returned with only '2'
The current query is:
SELECT DISTINCT id FROM table
EXCEPT
SELECT DISTINCT id FROM table WHERE status <> 'Off'
Which seems to work, but it's having to iterate over the entire table twice which ends up taking ~10-12 minutes to run per query. Is there a simpler way to do this with only a single query?
You can use WHERE NOT EXISTS instead:
Select Distinct Id
From Table A
Where Not Exists
(
Select *
From Table B
Where A.Id = B.Id
And B.Status = 'On'
)
I would also recommend looking at the indexes on the Status column. 10-12 minutes to run is excessively long. Even with 50m records, with proper indexing, a query like this shouldn't take longer than a second.
To add an index to the column, you can run this (I'm assuming SQL Server, your syntax may vary):
Create NonClustered Index Ix_YourTable_Status On YourTable (Status Asc);
You can use conditional aggregation.
select id
from table
group by id
having count(case when status='On' then 1 end)=0
You can use the help of a SELF JOIN ..
SELECT DISTINCT A.Id
FROM Table A
LEFT JOIN Table B ON A.Id=B.Id
WHERE B.Status='On'
AND B.Id IS NULL

Check if an account# exist in another database

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