INSERT INTO using multiple WITH clauses - sql

I want to insert record with a trigger. And the insert multiple values from different tables, so I am using multiple CTEs to hold values for them.
Basic structure of the trigger function is like this:
WITH prop1 AS (
...
), prop2 AS (
...
), prop3 As (
...
)
INSERT INTO table (..)
VALUES (prop1.attribute,prop2.attribute,prop3.attribute);
But this does not work.
The 'prop1.attribute' attribute fetching causing the fail.
How can I make this work?

You need to SELECT from your CTEs, and also join between them in some logic. I'll put CROSS JOIN in below example which will multiply every row from each CTE with every row from each of other other two
WITH prop1 AS (
...
), prop2 AS (
...
), prop3 As (
...
)
INSERT INTO table (..)
SELECT prop1.attribute,prop2.attribute,prop3.attribute
FROM prop1
CROSS JOIN prop2
CROSS JOIN prop3
Preferably replace CROSS JOINs with INNER:
FROM prop1
INNER JOIN prop2 ON prop2.someId = prop1.someId
INNER JOIN prop3 ON prop3.someId = prop1.someId

Related

Is it possible to use SELECT after WHERE

I have 2 tables: 1 master table and 1 that I'm trying to put together.
TABLE1: ID (AUTOINCREMENT) NAME PARENT
TABLE2: ID(AUTOINCREMENT) PARENT CHILD
SQL code:
INSERT INTO TABLE2 (PARENT, CHILD)
SELECT TABLE1.PARENT, TABLE1.ID
FROM TABLE1
WHERE TABLE1.PARENT = (SELECT DISTINCT TABLE1.PARENT FROM TABLE1)
So I want my query to go over all the PARENTs and one by one get all its children.
Obviously it's not working and it is returning only the first PARENT and its children IDs
Yes you can, using IN instead of =.
Try this:
INSERT INTO TABLE2 (PARENT, CHILD)
SELECT TABLE1.PARENT, TABLE1.ID
FROM TABLE1
WHERE TABLE1.PARENT IN (SELECT DISTINCT TABLE1.PARENT FROM TABLE1)
That query would produce the exact same results as the much simpler:
INSERT INTO TABLE2 (PARENT, CHILD)
SELECT PARENT, ID
FROM TABLE1
The subquery in the where clause has no effect. It's basically saying, please insert parent and id from table 1 where parent and id are in table 1. They will all be.
Now if you were using a separate table in the subquery this might make sense. But even then I would suggest simply joining like so:
INSERT INTO TABLE2 (PARENT, CHILD)
SELECT TABLE1.PARENT, TABLE1.ID
FROM TABLE1
JOIN
(SELECT DISTINCT PARENT FROM TABLE2) TABLE2
ON TABLE1.PARENT = TABLE2.PARENT

sql select query IN Clause returns nothing

Is it possible to get any value from the parameters passed inside IN clause where 1 value returns nothing.
Example:
select id, translation
from <table>
where id in (1,2,36)
and (locale_id='EN' )
If id 2 has no values in table, is it possible to return NULL in place of no values at all.
Currently it returns only 2 values.
try this:
with cte as (
select *
from (values (1),(2),(36) ) v(v)
)
select *
from cte
left join table1 t1 on t1.id=cte.v and (t1.locale_id='EN' )
you'll get at least on record per id
cte generates 3 records, each with one value v i prefer the syntax with cte over the following version (which should do the same)
select *
from (values (1),(2),(36)) v(v)
left join table1 t1 on t1.id=v.v and (t1.locale_id='EN' )
the left join well read yourself : http://www.w3schools.com/sql/sql_join_left.asp

What's different between INTERSECT and JOIN?

Create data :
CREATE TABLE sub1(id int,name nvarchar(7));
CREATE TABLE sub2(id int,name nvarchar(7));
INSERT INTO sub1 VALUES(1,'one1');
INSERT INTO sub2 VALUES(1,'one1');
INSERT INTO sub1 VALUES(2,'one2');
INSERT INTO sub2 VALUES(2,'one2');
INSERT INTO sub1 VALUES(3,'one3');
INSERT INTO sub2 VALUES(4,'one4');
INSERT INTO sub1 VALUES(5,'one5');
INSERT INTO sub2 VALUES(6,'one6');
INSERT INTO sub1 VALUES(NULL,NULL);
INSERT INTO sub2 VALUES(NULL,NULL);
What's different between these 2 queries:
SELECT * FROM sub1 INTERSECT SELECT * FROM sub2;
SELECT sub1.id,sub1.name FROM sub1 JOIN sub2 ON sub1.id = sub2.id;
What's different between INTERSECT and JOIN?
INTERSECT just compares 2 sets and picks only distinct equivalent values from both sets. It's important to note that NULL marks are considered as equals in INTERSECT set operator. Also sets should contain equal number of columns with implicitly convertible types.
Under Join i guess you mean INNER JOIN?
INNER JOIN will return you rows where matching predicate will return TRUE. I.E. if there are NULL marks in both tables those rows will not be returned because NULL <> NULL in SQL.
Also INTERSECT is just comparing SETS on all attributes. Their types should be implicitly convertible to each other. While in join you can compare on any predicate and different types of sets. It is not mandatory to return just rows where there are matches. For example you can produce cartesian product in join.
Select * from Table1
Join Table2 on 1 = 1

Is there a way to select Parent IDs in SQL without recursion or looping?

I have a table with the following columns: group_id, parent_id, name
In this table parent_id is the group_id of another record. There is a 1 to N relationship of parents to children. This forms a hierarchy with only one top level group having NULL for a parent_id. There could be an arbitrary amount of depth, but in practice my hierarchy is never more than 20 levels deep.
I would like to retrieve every ancestor (a parent's parent and so on) of a group with a given group_id. I am concerned about the particular way this is returned.
I am using MS SQL 2005, but I am also interested in solutions using other RDBMSs.
I have found some similar questions, but they all seem to break down to recursion, looping or nested sets. I cannot use nested sets, because I cannot change the data structure. I would like to avoid recursion or looping where possible, or at least understand why it is not possible.
Here are some question I found while researching this:
How to select parent ids
Sql recursion without recursion
The operation is inherently looped. Because each node does not have any finite relation to their root, you must traverse in order to discover it.
If, for example, you knew that there was a maximum depth of N then you could create N LEFT OUTER JOINs in a single statement and display the last non-null parent ID returned this way.
The looping requirement is that you simply don't know what N is, and you cannot ask a declarative language like SQL to "figure it out"
Even if you can accomplish it with some built-in method, it will still be a loop or recursion, just obfuscated from you.
You can create a temporary table similar to yours and populate it like this:
INSERT INTO #T(group_id, parent_id) SELECT group_id, parent_id FROM Your_Table
Now perform the exact same SQL five times:
INSERT INTO #T(group_id, parent_id) SELECT T2.group_id, T1.parent_id FROM #T T1 JOIN #T T2 ON T2.parent_id = T1.group_id LEFT JOIN #T C ON T2.group_id = C.group_id AND T1.parent_id = C.parent_id AND C.group_id IS NULL
INSERT INTO #T(group_id, parent_id) SELECT T2.group_id, T1.parent_id FROM #T T1 JOIN #T T2 ON T2.parent_id = T1.group_id LEFT JOIN #T C ON T2.group_id = C.group_id AND T1.parent_id = C.parent_id AND C.group_id IS NULL
INSERT INTO #T(group_id, parent_id) SELECT T2.group_id, T1.parent_id FROM #T T1 JOIN #T T2 ON T2.parent_id = T1.group_id LEFT JOIN #T C ON T2.group_id = C.group_id AND T1.parent_id = C.parent_id AND C.group_id IS NULL
INSERT INTO #T(group_id, parent_id) SELECT T2.group_id, T1.parent_id FROM #T T1 JOIN #T T2 ON T2.parent_id = T1.group_id LEFT JOIN #T C ON T2.group_id = C.group_id AND T1.parent_id = C.parent_id AND C.group_id IS NULL
INSERT INTO #T(group_id, parent_id) SELECT T2.group_id, T1.parent_id FROM #T T1 JOIN #T T2 ON T2.parent_id = T1.group_id LEFT JOIN #T C ON T2.group_id = C.group_id AND T1.parent_id = C.parent_id AND C.group_id IS NULL
After this, your table now tracks ancestors rather than parents, up to 32 levels of distance. (2^5 = 32 and 32 > 20).
This is one efficient way to compute "transitive closure", although if you add looping instead of just repeating the same INSERT five times, you will not need your assumption about 20 levels any more. You should just stop when the INSERT inserts zero new rows.
This kind of looping will help rather than hurt performance as well as the number of iterations will be very small.
If you know exactly how deep your data structure goes, you can just write the code out by hand:
DECLARE
#parentId1 int
,#parentId2 int
...
,#parentId19 int
,#parentId20 int
SELECT
#parentId1 = parent_id
FROM
myTable
WHERE
group_id = <someid>
SELECT
#parentId2 = parent_id
FROM
myTable
WHERE
group_id = #parentId1
And so on. However, this will give you a whole bunch of extra code and won't perform any better than a loop, and it's horribly brittle. Adding a new level to the tree requires you to amend your code, which should be an instant code smell.
Think about it in terms of any other language. You have to perform task X a total of N times, where N is variable. How are you going to go about writing this? You'd use a loop. Now suppose that your data structure is a tree (which is what you've got here). How would you write this? You'd probably use recursion, unless you flatten the recursion into a loop.
The only caveat specific to MSSQL is the fact that, by default, the recursion stack is limited to a depth of 16. You're much better off using loops than recursion in MSSQL.
I'd typically do something like this:
-- Temp table will hold the results starting from the ID of the source item
-- through all its ancestors in ascending order
DECLARE #table TABLE (
sequence int IDENTITY(1, 1)
,group_id int
)
DECLARE #groupId int
SELECT #groupId = <someid>
-- Loop backwards through the group's hierarchy inserting all parent IDs
-- into the temporary table
WHILE #groupId IS NOT NULL
BEGIN
INSERT INTO #table (
group_id
)
VALUES (
#groupId
)
-- Get the ID of the group's parent ready to loop again
SELECT #groupId = parent_id
FROM mutable
WHERE group_id = #groupId
END
-- Print the results
SELECT group_id
FROM #table
There are probably better ways, but that'll give you all IDs in a form you can manipulate easily, plus they'll be in the correct order.

SQL OUTER JOIN with NEWID to generate random data for each row

I want to generate some test data so for each row in a table I want to insert 10 random rows in another, see below:
INSERT INTO CarFeatures
(carID, featureID)
SELECT C.ID, F.ID
FROM dbo.Cars AS C
OUTER APPLY (
SELECT TOP 10 ID
FROM dbo.Features
ORDER BY NEWID()
) AS F
Only trouble is this returns the same values for each row. How do I order them randomly?
What i usually do is create a temp table and define the PK as a GUID with the default value of newid(). You'll need a create table statement for this, no select into. Then I insert my records into it and then I can order by the Id field and select the top ten.
The problem is that any function you call will be evaluated only once. How about something like this:
SELECT ID, NEWID() AS guid
INTO #temp
FROM dbo.Features
INSERT INTO CarFeatures (carID, featureID)
SELECT C.ID, F.ID
FROM dbo.Cars AS C
OUTER APPLY (
SELECT TOP 10 *
FROM #temp
ORDER BY 2
) AS F