How to insert values to table based on column value - sql

I have a table called BranchServices and its structure as follows,
FkBranchId FkServiceId SortValue
6 1 1
7 1 1
8 1 NULL
6 2 2
7 2 2
8 2 NULL
6 3 3
Each branch have its own Sort order (SortValue).Some branch haven't sort order and that branch SortValues are NULL.When I create a new service I insert values to this table through the cross join as follows,
INSERT INTO BranchServices
SELECT b.BranchId,
s.ServiceID,
NULL
FROM #insertedServiceID s
CROSS JOIN Branch b WHERE b.IsActive = 1
But I need to change above query.While inserting newly created service id to each branch, I need to check its current sort order. If that branch consists with SortValue = NULL then I need to insert SortValue as Null null and if that branch already have Sort order need to insert 0 to sortValue how to check that branch have sorting order or not before inserting newly created service. can I check it inside the cross join? so then how?

Use a subquery to get the SortValue for the FkBranchId that you insert:
INSERT INTO BranchServices
SELECT b.BranchId,
s.ServiceID,
CASE WHEN(SELECT MIN(SortValue) FROM BranchServices WHERE FkBranchId = b.BranchId) IS NOT NULL THEN 0 END
FROM #insertedServiceID s
CROSS JOIN Branch b WHERE b.IsActive = 1
I used MIN() to make sure that the subquery will return only 1 row (MAX() would also work).

Related

Why does my insert not detect existing rows in my target table?

I'm trying to implement SCD2 by using insert and update instead of using MERGE. I need to insert a new row into my target table if the matching ids have different hash values. The table contains id, name, hash value, and 1 as enabled, which entails that the rows is the most current version.
As of the moment, I'm not getting the expected output. For example, if I have the id “1” in both the target and source table but the hash value differs, it inserts the value if I run the query again into my target table, leaving me with the id “1” with many duplicate hash values.
Query:
INSERT INTO target
SELECT s.ID, s.namn, s.hashh, 1 AS enablee
FROM source s
JOIN target t ON s.id = t.id
WHERE s.hashh <> t.hashh
Output:
1 demo 222 0
1 demo 22220
1 demo 222 1
2 demo2 666 1
2 demo2 666 1
2 demo2 888 1
Expected output:
1 demo 222 1
1 demo 22220
2 demo2 666 1
2 demo2 888 0
Ideally, I would like the insertion not to work and give me the output: (0 rows affected) if the hash value already exists in the targeted table.
To understand the behavior, consider than WHERE applies to SELECT statement, not INSERT.
You can just run
SELECT s.ID, s.namn, s.hashh, 1 AS enablee
FROM source s
JOIN target t ON s.id = t.id
WHERE s.hashh <> t.hashh
to see what is inserted. The join finds all rows with same id, and mismatched hash. If all hashes match, it produces no result. But if there are some rows with mismatched hash, you get the results even if there is a matching row too.
What you need is the opposite, join on matched hash only and check if you found a match. Something like
SELECT s.ID, s.namn, s.hashh, 1 AS enablee
FROM source s
LEFT JOIN target t ON s.id = t.id
AND s.hashh = t.hashh
WHERE t.hashh IS NULL

Get Sequence of Rows (Linked-List) in PostgreSQL

I have a table called deliveries. Some of these deliveries are a part of a sequence (having a parent or child or both), and some of these deliveries are one-offs.
id parent_delivery_id child_delivery_id
---------------------------------------------
1 NULL 2
2 1 3
3 2 4
4 3 NULL
5 NULL NULL
6 NULL NULL
7 NULL 8
8 7 NULL
Using the example above, I would like to write a bit of SQL to grab all of the deliveries in the sequence starting with delivery 1 and ending with delivery 4.
Expected selection:
id parent_delivery_id child_delivery_id
---------------------------------------------
1 NULL 2
2 1 3
3 2 4
4 3 NULL
I used this solution based on what I found here:
Get Row's Sequence (Linked-List) in PostgreSQL
WITH RECURSIVE pathtobottom AS (
-- Get the path from element to bottom list following next element id that matches current link_id
SELECT 1 i, -- add fake order column to reverse retrieved records
* FROM deliveries WHERE deliveries.id = 1
UNION ALL
SELECT pathtobottom.i + 1 i, -- add fake order column to reverse retrieved records
recursive.* FROM deliveries recursive
INNER JOIN pathtobottom ON recursive.id = pathtobottom.parent_delivery_id
)
, pathtotop AS (
-- Get the path from element to top list following previous element link_id that matches current id
SELECT 1 i, -- add fake order column to reverse retrieved records
* FROM deliveries WHERE deliveries.id = 1
UNION ALL
SELECT pathtotop.i + 1 i, -- add fake order column to reverse retrieved records
recursive2.* FROM deliveries recursive2
INNER JOIN pathtotop ON recursive2.parent_delivery_id = pathtotop.id
), pathtotoprev as (
-- Reverse path to top using fake 'i' column
SELECT pathtotop.id FROM pathtotop order by i desc
), pathtobottomrev as (
-- Reverse path to bottom using fake 'i' column
SELECT pathtobottom.id FROM pathtobottom order by i desc
)
-- Elements ordered from bottom to top
SELECT pathtobottomrev.id FROM pathtobottomrev where id != 1 -- remove element to avoid duplicate
UNION ALL
SELECT pathtotop.id FROM pathtotop;

Count number of repeats in SQL

I tried to solve one problem but without success.
I have two list of number
{1,2,3,4}
{5,6,7,8,9}
And I have table
ID Number
1 1
1 2
1 7
1 2
1 6
2 8
2 7
2 3
2 9
Now I need to count how many times number from second list come after number from first list but I should count only one by one id
in example table above result should be 2
three matched pars but because we have only two different IDs result is 2 instead 3
Pars:
1 2
1 7
1 2
1 6
2 3
2 9
note. I work with MSSQL
Edit. There is one more column Date which determined order
Edit2 - Solution
i write this query
SELECT * FROM table t
left JOIN table tt ON tt.ID = t.ID
AND tt.Date > t.Date
AND t.Number IN (1,2,3,4)
AND tt.Number IN (6,7,8,9)
And after this I had a plan to group by id and use only one match for each id but execution take a lot time
Here is a query that would do it:
select a.id, min(a.number) as a, min(b.number) as b
from mytable a
inner join mytable b
on a.id = b.id
and a.date < b.date
and b.number in (5,6,7,8,9)
where a.number in (1,2,3,4)
group by a.id
Output is:
id a b
1 1 6
2 3 9
So the two pairs are output each on one line, with the value a belonging to the first group of numbers, and the value of column b to the second group.
Here is a fiddle
Comments on attempt (edit 2 to question)
Later you added a query attempt to your question. Some comments about that attempt:
You don't need a left join because you really want to have a match for both values. inner join has in general better performance, so use that.
The condition t.Number IN (1,2,3,4) does not belong in the on clause. In combination with a left join the result will include t records that violate this condition. It should be put in the where clause.
Your concern about performance may be warranted, but can be resolved by adding a useful index on your table, i.e. on (id, number, date) or (id, date, number)

Find next unused ID in table SQL which has non continous ranges

I have two tables like the following:
TABLE1:
=======
somid, tobeupdated
1 , null
2 , null
3 , null
10 , null
TABLE2:
=======
rangeofids
2
3
9
10
11
12
13
I have to update TABLE1.tobeupdated (or found its' should be value) based on the following criteria(s):
if TABLE1.somid NOT exists in TABLE2.rangeofids, then the expected result is: tobeupdated = TABLE1.somid
else find the next available (or unused) TABLE2.rangeofids which is larger then TABLE1.somid
So the expected values are:bu
TABLE1:
=======
somid, tobeupdated
1 , 1
2 , 4
3 , 4
10 , 14
I tried hard, but the simplest solution I came up with is creating a temporary table with a full sequence of ids (from 1 to max(rangeofids)+1) MINUS TABLE2.rangeofids so I can found the MIN(TMPTABLE.id) where TMPTABLE.ID > TABLE1.somid.
But isn't there a better solution (without the temp table)?
Note: I can't create procedures/functions, etc, so it must be standard (Oracle 10) SQL.
This is my try.
First we should decide using only table2 what value should return after finding the value there.
select rangeofids,
candidate,
nvl(candidate,lead(candidate ignore nulls) over (order by rangeofids)) as full_candidate
from (
select rangeofids, case when dist=1 then null else rangeofids+1 end as candidate
from (
select rangeofids,
lead(rangeofids) over (order by rangeofids) - rangeofids as dist
from table2
)
);
After this a merge into table1 with the below select will solve the problem:
select someid, nvl(full_candidate, someid)
from table1 a
left join (
--the above query
) b
on a.someid = b.rangeofids;
See SQLFIDDLE.

How do I use a join to query two tables and get all rows from one, and related rows from the other?

Simplified for example, I have two tables, groups and items.
items (
id,
groupId,
title
)
groups (
id,
groupTitle,
externalURL
)
The regular query I'm goes something like this:
SELECT
i.`id`,
i.`title`,
g.`id` as 'groupId',
g.`groupTitle`,
g.`externalURL`
FROM
items i INNER JOIN groups g ON (i.`groupId` = g.`id`)
However I need to modify this now, because all the groups which specify an externalURL will not have any corresponding records in the items table (since they're stored externally). Is it possible to do some sort of join so that the output looks kinda like this:
items:
id title groupId
----------------------
1 Item 1 1
2 Item 2 1
groups
id groupTitle externalURL
-------------------------------
1 Group 1 NULL
2 Group 2 something
3 Group 3 NULL
Query output:
id title groupId groupTitle externalURL
---------------------------------------------------
1 Item 1 1 Group 1 NULL
2 Item 2 1 Group 1 NULL
NULL NULL 2 Group 2 something
-- note that group 3 didn't show up because it had no items OR externalURL
Is that possible in one SQL query?
This is exactly what an outer join is for: return all the rows from one table, whether or not there is a matching row in the other table. In those cases, return NULL for all the columns of the other table.
The other condition you can take care of in the WHERE clause.
SELECT
i.`id`,
i.`title`,
g.`id` as 'groupId',
g.`groupTitle`,
g.`externalURL`
FROM
items i RIGHT OUTER JOIN groups g ON (i.`groupId` = g.`id`)
WHERE i.`id` IS NOT NULL OR g.`externalURL` IS NOT NULL;
Only if both i.id and g.externalURL are NULL, then the whole row of the joined result set should be excluded.