sql query to update parent with more than two children - sql

I have two tables like these:
parents(id:int, name:string, large_family:boolean default false)
children(id:int, name:string, parent_id:foreign key to parents)
how can i write a query to update large_family field in parent to true if a parent has more than two children.
thank you

In most databases, you can do this with a correlated subquery:
update parents
set large_family = 1
where (select count(*) from children c where c.parent_id = parent.id) > 2

You can do this with a subquery in the SET set clause to set the large_family field for all rows, or you could use a subquery in the WHERE clause to only set it for rows with more than 2 children. Here is an example using the SET clause:
UPDATE p
SET p.large_family = CASE WHEN (SELECT COUNT(*) FROM children c WHERE c.parent_id = p.id) > 2 THEN 1 ELSE 0 END
FROM parents p

Related

what is wrong with my sql query (case when exists)

I'm trying to understand how case when exists expression works, there is two tables one is trOrderHeader which stores main info about any order. Other is trOrderLine which stores details about an order and there is a column IsClosed which indicates Order is somehow closed (canceled or completed).
So my query is below which I am trying to get OrderLineID with IsClosed column (I can do it by joins it is easy but I am trying case when exists expression) but all IsClosed column returns 1;
SELECT
OrderLineId,
IsClosed =
CASE
WHEN EXISTS (select * from trOrderHeader where IsClosed=1)
THEN 1
WHEN EXISTS (select * from trOrderHeader where IsClosed=0)
THEN 0
END
FROM
trOrderLine
GROUP BY OrderLineId
Isn't this supposed to give me if an Order is Closed write 1, if an Order is not Closed write 0?
Did I misunderstand case when exists expression?
You would need to correlate the subqueries. Assuming that column OrderId can be used to relate the tables, then:
SELECT
OrderLineId,
IsClosed =
CASE
WHEN EXISTS (select 1 from trOrderHeader h where h.IsClosed = 1 AND h.OrderId = l.OrderId)
THEN 1
WHEN EXISTS (select 1 from trOrderHeader h where IsClosed = 0 AND h.OrderId = l.OrderId)
THEN 0
END
FROM trOrderLine l
GROUP BY OrderLineId
However, the logic of the query could probably be simplified by using a JOIN and an aggregate function to compute the status. Something like this could be what you need:
SELECT l.OrderLineId, MAX(h.isClosed) IsClosed
FROM trOrderLine l
INNER JOIN trOrderHeader h ON h.OrderId = l.OrderId
GROUP BY l.OrderLineId

SQL - where condition for multple child tables

I have some 10 tables in which 1 is parent and other 9 are parallel children.
All these 9 tables have a column named Version with values 0 & above. zero is draft.
I tried with JOINS but with joins I got ambiguity for Version Column.
Is there any way where I can say that any of these 9 child tables has any drafts
Required output is Parent table columns + HasDrafts (From child tables).
Is there any way to achieve this ? If yes then guide me please.
If you don't care which table has the drafts or how many, then you can use exists in a case expression:
select p.*,
(case when exists (select 1 from child1 c where c.parentid = p.parentid and c.version = 0) or
exists (select 1 from child2 c where c.parentid = p.parentid and c.version = 0) or
exists (select 1 from child3 c where c.parentid = p.parentid and c.version = 0) or
. . .
then 1 else 0
end) as has_drafts
from parent p;
I would have made a union and set an extra column for which table it is Ex.
Select ID, Version, 'tableA' from TableA
union
Select ID, Version, 'tableB' from TableB
Thats my dirty solution.

Selective update in SQL Server

I've created a junction table like this one:
http://imageshack.us/scaled/landing/822/kantotype.png
I was trying to figure out a query that could able to select some rows - based on the PokémonID - and then updating only the first or second row after the major "filtering".
For example:
Let's suppose that I would like to change the value of the TypeID from the second row containing PokémonID = 2. I cannot simply use UPDATE KantoType SET TypeID = x WHERE PokémonID = 2, because it will change both rows!
I've already tried to use subqueries containing IN,EXISTS and LIMIT, but with no success.
Its unclear what are your trying to do. However, you can UPDATE with JOIN like so:
UPDATE
SET k1.TypeID = 'somethng' -- or some value from k2
FROM KantoType k1
INNER JOIN
(
Some filtering and selecting
) k2 ON k1.PokémonID = k2.PokémonID
WHERE k1.PokémonID = 2;
Or: if you want to UPDATE only the two rows that have PokémonID = 2 you can do this:
WITH CTE
AS
(
SELECT *,
ROW_NUMBER() OVER(ORDER BY TypeID) rownum
FROM KantoType
WHERE PokemonID = 2
)
UPDATE c
SET c.TypeID = 5
FROM CTE c
WHERE c.rownum = 1;
SQL Fiddle Demo
I can suggest something like this if you just need to update a single line in your table:
UPDATE kantotype
SET
type = 2
WHERE pokemon = 2
AND NOT EXISTS (SELECT * FROM kantotype k2
WHERE kantotype.type > k2.type
AND kantotype.pokemon = k2.pokemon)
It would be easier to get the first or last item of the table if you had unique identifier field in your table.
Not sure even if you are trying to update the row with PokemenID =2 by doing a major filtering on TypeID... So just out of assumptiong (big one), you can give a try on Case
UPDATE yourtable a
LEFT JOIN youtable b on a.pokeid = b.pokeid
SET a.typeid = (CASE
WHEN a.typeid < b.typeid THEN yourupdatevalue
WHEN a.typeid > b.typeid THEN someothervalue
ELSE a.typeid END);
If you know the pokemon ID and the type id then just add both to the where clause of your query.
UPDATE KantoType
SET TypeID = x
WHERE PokémonID = 2
AND TypeID=1
If you don't know the type ID, then you need to provide more information about what you're trying to accomplish. It's not clear why you don't have this information.
Perhaps think about what is the unique identifier in your data set.

Exclude results with join conditions

I'm trying to make an sql request with join exclusion.
Explains:
Table element
id # name #
1 Sea
2 tree
Table colour
id # name #
1 green
2 blue
3 brown
Table relation
element_id # colour_id
1 2
2 1
2 3
I have my working request for "get elements for one of these colours".
Exemple with green and blue:
SELECT element.name, colour.name FROM element
LEFT JOIN relation
ON (element.id = relation.element_id)
LEFT JOIN colour
ON (colour.id = relation.colour_id)
WHERE (relation.colour_id = 1 OR relation.colour_id = 2)
I would like make request for "get elements where they have a relation with all listed colors". Where for green and brown it returns tree.
I've tried to change the 'OR' to 'AND' but request return 0 results :/
General way to solve this problem is to filter values and count how many times they appear in result. If equal, all elements are found.
select element_id
from relation
where colour_id in (1, 2)
group by element_id
having count (distinct colour_id) = 2
Having this table one might join it to original tables to produce full column set:
SELECT element.name, colour.name
FROM relation
INNER JOIN
(
select element_id
from relation
where colour_id in (1, 2)
group by element_id
having count (distinct colour_id) = 2
) matches
ON relation.element_id = matches.element_id
INNER JOIN element
ON element.id = relation.element_id
INNER JOIN colour
ON colour.id = relation.colour_id
This type of query can be handled with some of SQL's set-based operators:
Which elements have relations for all colours?
Using the ALL operator (syntax may vary slightly by database):
SELECT element.name
FROM element
WHERE ( SELECT colour.id FROM relation
INNER JOIN colour ON colour.id = relation.colour_id
WHERE relation.element_id = element.id )
= ALL ( SELECT colour.id from colour)
;
Using the EXCEPT operator:
SELECT element.name
FROM element
WHERE NOT EXISTS
( SELECT colour.id from colour
EXCEPT
SELECT colour.id FROM relation
INNER JOIN colour ON colour.id = relation.colour_id
WHERE relation.element_id = element.id
)
;
There is a typo in your WHERE clause - one of the ids is 2 ("blue") instead of 3 ("brown"). It should be
WHERE (relation.colour_id = 1 OR relation.colour_id = 3)
(or in shorter form:
WHERE relation.colour_id IN (1, 3)
).
Note however, that your current query - although after this fix it should work for your sample data - won't give you correct results in general. It will give you the elements associated with any of the specified colors. The correct solution to this is given in #Nikola's answer though.
without subselects, I would suggest:
SELECT
e.id AS id
FROM
element AS e
LEFT OUTER JOIN relation AS r ON r.element_id = e.id
GROUP BY e.id HAVING SUM(CASE WHEN r.colour_id = 1 THEN 1 ELSE 0 END ) = 0
ORDER BY e.id ASC;
after this you can select the elements by id.

Select count(*) or zero

Tiredness prevents me from finding this one... Say you have the following tables:
Parent
PARENT_ID (LONG)
Child
CHILD_ID (LONG)
PARENT_ID (LONG, FK)
HAS_GRADUATED (BOOLEAN)
I want a query to return the following true (1, in the case of Oracle) if the parent has at least one child that has graduated, and false (0, in the case if Oracle) if the parent does not have a child that has graduated, or has no children at all:
PARENT_ID................HAS_CHILDREN_WHO_GRADUATED
5.................................1
3.................................1
6.................................0
2.................................0
In the above, parent with parent_id=5 may have >=1 children that have graduated. Same is parent with parent_id=3. Parent with parent_id=6 either has no children at all, or has children but none of them has graduated.
What would the query to this be like?
Use:
SELECT DISTINCT
p.parent_id,
CASE WHEN c.parent_id IS NULL THEN 0 ELSE 1 END
FROM PARENT p
LEFT JOIN CHILD c ON c.parent_id = p.parent_id
AND c.has_graduated = 1
You have to use an outer join in order to see the parent values that don't have supporting records in the child table.
Will this give you what you expect?
SELECT
P.Parent_Id,
CASE WHEN (SUM (CASE WHEN Has_Graduated = 1 then 1 else 0 END)) = 0 THEN 0 ELSE 1 as HAS_CHILDREN_WHO_GRADUATED
FROM Parent P
LEFT JOIN Child C
ON P.Parent_Id = C.Parent_Id
GROUP BY P.Parent_Id
It is likely that OMG Ponies solution will perform better (which is why he got my +1), but this yet another way of solving the problem.
Select Parent_Id
, Case
When Exists( Select 1
From Child
Where Child.Parent_Id = Parent.Parent_Id
And Child.Has_Graduated = 1 ) Then 1
Else 0
End
From Parent
First of all I don't think you can use LONG columns for this since LONG values cannot be used in WHERE conditions. Note this is true as of 10g, since that's what I use.
Second I assume you mean that your child table should have a column called PARENT_ID otherwise there would be no way to link the two tables. Given that, this query ought to work:
SELECT PARENT_ID, COUNT(1) FROM Child WHERE HAS_GRADUATED = 1 GROUP BY PARENT_ID
Here's the form of the query, though the syntax for Oracle may be off:
SELECT
Parent.PARENT_ID
,case count(Child.PARENT_ID) when 0 then 0 else 1 end HAS_CHILDREN_WHO_GRADUATED
from Parent
left outer join Child
on Child.PARENT_ID = Parent.PARENT_ID
where Child.HAS_GRADUATED = 1
group by Parent.PARENT_ID
This will list all Parent items once, with HAS_CHILDREN_WHO_GRADUATED
set to 1 or 0 set as desired.
(Edited to add the where clause)