To update a column by checking the value from another column in a different table - sql

I am trying to update flag in my main table based on the flag in another common table.Both are related with the Foreign Key relationship. But the problem is the flag in another common table is either 0 or 1. So, it should update the flag in the main table as 1 only if all the values for a particular FK is 1.
Suppose that there are 2 tables listed below. XYZ and ABC. Both are related to each other through Foreign Key.
XYZ:
XYZID Posted
1 0
2 0
3 0
4 0
ABC:
ABCID XYZID IsPosted
1 1 1
2 1 1
3 2 0
4 2 0
5 2 0
6 3 1
7 3 0
8 4 0
9 4 0
10 4 1
If you see for XYZID in ABC table the Isposted value is 1 for both. I want that value to be updated in the Posted as 1 of XYZ main table for XYZID 1. But if you look at XYZID value 3 in ABC table for IsPosted then it is 0 and 1. So for XYZID value 3 the Posted value should not be updated in the XYZ table as 1. In general, if all the foreign key value has the IsPosted as 1 then only it should be updated as 1 in the Posted column of XYZ table. If it is 0 or 1 then it should not update in the XYZ table.
I thought of using group by or cursor. But don't know how to start on this.
If anyone can help me in this then would be helpful. It is pretty simple but I am not getting the idea to start on this. Any help would be appreciated.

Update the table by joining a subquery that groups by xyzid the table abc and sets the condition in the having clause:
update t
set posted = 1
from xyz t inner join (
select xyzid from abc
group by xyzid
having sum(case when isposted = 0 then 1 else 0 end) = 0
) a on a.xyzid = t.xyzid
The condition in the having clause could also be written:
having sum(abs(isposted - 1)) = 0
See the demo.
Results:
> XYZID | Posted
> ----: | -----:
> 1 | 1
> 2 | 0
> 3 | 0
> 4 | 0

Assuming there can only be 0 or 1, one way is to use a correlated subquery getting the minimum isposted for an xyzid.
UPDATE main_table
SET posted = (SELECT min(another_common_table.isposted)
FROM another_common_table
WHERE another_common_table.xyzid = main_table.xyzid);
If there is a 0 the minimum will be 0. If there's only 1s it'll be 1.

Try the following:
UPDATE [a]
SET a.[Posted] = [b].[IsPosted]
FROM [a]
INNER JOIN (SELECT [xyzid],
[IsPosted] = MIN(Cast([IsPosted] AS INT))
FROM
[b]
GROUP BY
[xyzid]
HAVING
MIN(Cast([IsPosted] AS INT)) = 1) [b]
ON [a].[xyzid] = [b].[xyzid]
Essentially, the inner query returns only those entries from table B with all 1 values and then updates the A table based on the FK join.
There may be more efficient queries AND this will re-update previously updated A.Posted values and will NOT un-update A.Posted if anything in table B is marked as IsPosted = 0.

Related

How to check the count of each values repeating in a row

I have two tables. Data in the first table is:
ID Username
1 Dan
2 Eli
3 Sean
4 John
Second Table Data:
user_id Status_id
1 2
1 3
4 1
3 2
2 3
1 1
3 3
3 3
3 3
. .
goes on goes on
These are my both tables.
I want to find the frequency of individual users doing 'status_id'
My expected result is:
username status_id(1) status_id(2) status_id(3)
Dan 1 1 1
Eli 0 0 1
Sean 0 1 2
John 1 0 0
My current code is:
SELECT b.username , COUNT(a.status_id)
FROM masterdb.auth_user b
left outer join masterdb.xmlform_joblist a
on a.user1_id = b.id
GROUP BY b.username, b.id, a.status_id
This gives me the separate count but in a single row without mentioning which status_id each column represents
This is called pivot and it works in two steps:
extracts the data for the specific field using a CASE statement
aggregates the data on users, to make every field value lie on the same record for each user
SELECT Username,
SUM(CASE WHEN status_id = 1 THEN 1 END) AS status_id_1,
SUM(CASE WHEN status_id = 2 THEN 1 END) AS status_id_2,
SUM(CASE WHEN status_id = 3 THEN 1 END) AS status_id_3
FROM t2
INNER JOIN t1
ON t2.user_id = t1._ID
GROUP BY Username
ORDER BY Username
Check the demo here.
Note: This solution assumes that there are 3 status_id values. If you need to generalize on the amount of status ids, you would require a dynamic query. In any case, it's better to avoid dynamic queries if you can.

How to use a new column as a flag to show Null row?

I have table A:
id
1
2
3
4
5
and table B:
id
2
3
4
I left join A and B:
id id
1 NULL
2 2
3 3
4 4
5 NULL
And how can I get a new column like this:
id id flag
1 NULL 0
2 2 1
3 3 1
4 4 1
5 NULL 0
Generally speaking, I want all rows in A but not in B to be flaged as 0 and want all rows in both tables to be flaged as 1. How can I achieve that? Better not use CTE.
This is just a CASE expression:
CASE WHEN B.id IS NULL THEN 0 ELSE 1 END AS flag
Alternatively, you could use an IIF (which is shorthand CASE expression):
IIF(b.id IS NULL, 0,1)
I would recommend using exists:
select a.*,
(case when exists (select 1 from b where b.id = a.id
then 1 else 0
end) as flag
from a;
The purpose of using exists instead of left join is that you are guaranteed to not get duplicate rows -- even if ids are duplicated in b. That is a nice guarantee.
From a performance perspective, the two should be similar, but it is possible that the case is an iota faster.

How to arrange Sql rows in a specific format

I have table with up to 50 rows... like given below.
ID menu dispOdr ParntID
---------------------
1 abc 1 0
2 cde 2 0
3 fgh 1 2
4 ghdfdj 2 2
5 tetss 1 1
6 uni 3 0
but I want to be sorted
Like
ID menu dispOdr ParntID
---------------------
1 abc 1 0
5 tetss 1 1
2 cde 2 0
3 fgh 1 2
4 ghdfdj 2 2
6 uni 3 0
If have any query please let me know.. thanks in advance.
I am using sql server 2014
I think you need your current vs. desired output reversed. You say you want the menu column sorted, but it appears that it already is.
So assuming you are actually starting with the second table, you can sort the menu column simply using ORDER BY:
SELECT *
FROM mytable
ORDER BY menu ASC
I think that the query below produces the required output:
SELECT t1.ID, t1.menu, t1.dispOdr, t1.ParntID
FROM mytable AS t1
LEFT JOIN mytable AS t2 ON t1.ParntID = t2.ID
ORDER BY CASE
WHEN t1.ParntID = 0 THEN t1.dispOdr
ELSE t2.dispOdr
END,
CASE
WHEN t1.ParntID = 0 THEN 1
ELSE 2
END,
t1.dispOdr
The first CASE expression groups records according to the dispOdr of their parent. The second CASE places parent on the top of its subgroup. Finally, the last expression used in the ORDER BY clause orders all child records within a subgroup.
Note: The above query works with one level of nesting.

How to update one table based on aggregate query form another table

Say I have two tables.
Table A
id
A_status
parent_id_B
Table B
id
B_status
So for each id in B can have many records in A.
Now my question is, I need to set B_status to 1 when all child entries in Table A with same parent_id_B has A_status =1, else set B_status = 2
Ex:
Table A:
id A_status parent_id_B
1 1 1
2 1 1
3 1 2
4 1 3
5 1 3
Table B:
id B_status
1 0
2 0
3 0
Expected result:
Table B:
id B_status
1 1
2 1
3 1
Now consider another scenario
Table A:
id A_status parent_id_B
1 1 1
2 1 1
3 2 2
4 2 3
5 1 3
Table B:
id B_status
1 0
2 0
3 0
Expected result:
Table B:
id B_status
1 1
2 2
3 2
I need this to work only on sqlite. Thanks
I believe this can be done like so:
UPDATE TableB
SET B_Status =
(SELECT MAX(A_Status) FROM TableA WHERE TableA.Parent_ID_B = TableB.ID);
SqlFiddle with your second case here
In a more general case (without relying on direct mapping of A's status, you can also use a CASE ... WHEN in the mapping:
UPDATE TableB
SET B_Status =
CASE WHEN (SELECT MAX(A_Status)
FROM TableA
WHERE TableA.Parent_ID_B = TableB.ID) = 1
THEN 1
ELSE 2
END;
Edit (in the case where there are more than the original number of states):
I believe you'll need to determine 2 facts about each row, e.g.
Whether there is are any rows in table A with a status other than 1 for each B
And there must at least be one row for the same B
Or, whether the count of rows of A in state 1 = the count of all rows in A for the B.
Here's the first option:
UPDATE TableB
SET B_Status =
CASE WHEN
EXISTS
(SELECT 1
FROM TableA
WHERE TableA.Parent_ID_B = TableB.ID
AND TableA.A_Status <> 1)
OR NOT EXISTS(SELECT 1
FROM TableA
WHERE TableA.Parent_ID_B = TableB.ID)
THEN 2
ELSE 1
END;
Updated Fiddle

Inserting a new indicator column to tell if a given row maximizes another column in SQL

I currently have a table in SQL that looks like this
PRODUCT_ID_1 PRODUCT_ID_2 SCORE
1 2 10
1 3 100
1 10 3000
2 10 10
3 35 100
3 2 1001
That is, PRODUCT_ID_1,PRODUCT_ID_2 is a primary key for this table.
What I would like to do is use this table to add in a row to tell whether or not the current row is the one that maximizes SCORE for a value of PRODUCT_ID_1.
In other words, what I would like to get is the following table:
PRODUCT_ID_1 PRODUCT_ID_2 SCORE IS_MAX_SCORE_FOR_ID_1
1 2 10 0
1 3 100 0
1 10 3000 1
2 10 10 1
3 35 100 0
3 2 1001 1
I am wondering how I can compute the IS_MAX_SCORE_FOR_ID_1 column and insert it into the table without having to create a new table.
You can try like this...
Select PRODUCT_ID_1, PRODUCT_ID_2 ,SCORE,
(Case when b.Score=
(Select Max(a.Score) from TableName a where a.PRODUCT_ID_1=b. PRODUCT_ID_1)
then 1 else 0 End) as IS_MAX_SCORE_FOR_ID_1
from TableName b
You can use a window function for this:
select product_id_1,
product_id_2,
score,
case
when score = max(score) over (partition by product_id_1) then 1
else 0
end as is_max_score_for_id_1
from the_table
order by product_id_1;
(The above is ANSI SQL and should run on any modern DBMS)