I'm currently working on a stored procedure in T-SQL on SQL Server 2012. The task is really tricky: I need to merge (insert,update,delete) entries in multiple tables.
To facilitate, I try to explain my problem with 2 tables. I use Dapper in C# in an ASP.NET 4.5 MVC application and can control my Front-End. I'm passing into the stored procedure 2 Temporary Tables:
Temporary Table Target:
ID | TargetId | TargetGroupId | IsPublic | IsPrivate |
---+----------+---------------+----------+-----------+
0 | 42 | 1 | 0 | 1 |
1 | 0 | 1 | 1 | 0 |
2 | 0 | 1 | 0 | 1 |
Temporary Table Target Countries
ID |CountryId | TargetId |
---+----------+----------+
0 | CA | 42 |
1 | FR | 0 |
2 | AU | 0 |
The TargetId 0 corresponds to a new entry, thus if the TargetId = 0 I want to Insert or Delete an old record not from the list, if the TargetId > 0 I want to Update the record.
The final Result could look like this:
Table Target:
| TargetId | TargetGroupId | IsPublic | IsPrivate |
+----------+---------------+----------+-----------+
| 42 | 1 | 0 | 1 |
| 93 | 1 | 1 | 0 |
| 94 | 1 | 0 | 1 |
Table Country:
CId |CountryId | TargetId |
-----+----------+----------+
34 | CA | 42 |
35 | FR | 93 |
36 | AU | 94 |
So the new TargetId should be stored in Country as well. My current TSQL script looks like this:
DECLARE #TargetGroupId = 1;
MERGE [MySchema].[Target] AS [A]
USING #Targets AS [B]
ON ([A].TargetId = [B].TargetId)
WHEN NOT MATCHED BY TARGET THEN
INSERT (TargetGroupId, IsPrivate, IsPublic)
VALUES (#TargetGroupId, [B].IsPrivate, [B].IsPublic)
-- Desired behaviour: doesn't work unfortunately...
-- OUTPUT inserted.TargetId, [B].ID
-- INTO #tmp;
WHEN MATCHED THEN
UPDATE
SET [A].IsPrivate = [B].IsPrivate, [A].IsPublic = [B].IsPublic
WHEN NOT MATCHED BY SOURCE AND [A].TargetGroupId = #TargetGroupId
THEN DELETE;
This SQL script works fine for my table Target, but how can I update my second table Countries?
Do you know how solve this issue, thus to Insert, Update, Delete in a Merge Statement including several tables?
Thank you very much!!
Related
I have a table dealers which has column token(need to be unique) and has isTokenActive(bool).
I was wandering if there is a way to add unique constraint on a token column but only if there is another row that has same token and isTokenActive is set to 1. If there is a row with same token and isTokenActive is set to 0 that is a valid case too.
Basically I want to allow this
id | Token | isTokenActive |
--------------------------------------
1 | ABC | 0 |
2 | ABC | 1 |
And this
id | Token | isTokenActive |
--------------------------------------
1 | ABC | 0 |
2 | ABC | 0 |
But not this
id | Token | isTokenActive |
--------------------------------------
1 | ABC | 1 |
2 | ABC | 1 |
And it would be great if it is possible to do it code first with EF Core.
I am attempting to filter results of a select statement using checkboxes and parameters with a SqlDataSource control.
The problem is that parameters can't be undefined, and I only want any of the given parameters to equal "1" or "True" if they are defined. Any other time, they shouldn't be used in the WHERE clause at all (or maybe set to something like "IS NOT NULL").
Essentially, when a checkbox is checked, the WHERE clause should include a parameter associated with that checkbox and the value of the parameter should be "1" (True), otherwise, if a checkbox is in any other state, "unchecked" or "indeterminate" the WHERE clause should not include a parameter for that checkbox at all, or it should be set to some value that essentially selects either "1" (True) or "0" (False).
I have tried playing with the WHERE clause by adding parenthesis, adding "AND" and "OR" statements, but I can't seem to figure it out. Dynamic SQL would easily solve this problem, but it would be a nightmare to maintain and it's just bad practice.
As an example, let's say you have the following data set:
| ID | Col1 | BitField1 | BitField2 | BitField3 |
| 1 | Row1 | 1 | 0 | 1 |
| 2 | Row2 | 1 | 1 | 1 |
| 3 | Row3 | 1 | 0 | 1 |
| 4 | Row4 | 1 | 1 | 0 |
| 5 | Row5 | 0 | 0 | 0 |
On our UX we would have 3 checkboxes, one for each "BitField" in the data.
If the user checks none of the checkboxes, the search result should return:
| ID | Col1 | BitField1 | BitField2 | BitField3 |
| 1 | Row1 | 1 | 0 | 1 |
| 2 | Row2 | 1 | 1 | 1 |
| 3 | Row3 | 1 | 0 | 1 |
| 4 | Row4 | 1 | 1 | 0 |
| 5 | Row5 | 0 | 0 | 0 |
and the WHERE statement should essentially have no parameters, or if it does, it would be something like WHERE BitField1 IS NOT NULL AND BitField2 IS NOT NULL AND BitField3 IS NOT NULL.
If the user checks the box associated with "BitField2" the result should be:
| ID | Col1 | BitField1 | BitField2 | BitField3 |
| 2 | Row2 | 1 | 1 | 1 |
| 4 | Row4 | 1 | 1 | 0 |
and the WHERE statement should be WHERE BitField2 = 1.
If the user checks the boxes associated with "BitField1" and "BitField2" the result should be:
| ID | Col1 | BitField1 | BitField2 | BitField3 |
| 2 | Row2 | 1 | 1 | 1 |
| 4 | Row4 | 1 | 1 | 0 |
and the WHERE statement should be WHERE BitField1 = 1 AND BitField2 = 1.
If the user checks all the boxes, the result should be:
| ID | Col1 | BitField1 | BitField2 | BitField3 |
| 2 | Row2 | 1 | 1 | 1 |
and the WHERE statement should be WHERE BitField1 = 1 AND BitField2 = 1 AND BitField3 = 1.
Is this something I can do without using dynamic SQL? I'm not using a stored procedure, but a standard TSQL query.
Any help would be GREATLY appreciated.
If a box is not checked make it's parameter value DBNull.Value. Then in the WHERE clause do this for each
... AND BitFieldX = ISNULL(#BitFieldX, [BitFieldx])...
This has the effect of using 1 or else whatever the bitfield is which effectively negates any filter on that field.
I have the following tables:
Sales Table:
+----+------------+
| ID | Date |
+----+------------+
| 1 | 11/20/2018 |
| 2 | 11/21/2018 |
+----+------------+
Items Table:
+----+------------+----------+-------+----------+
| ID | FK_Sale_ID | Quantity | Price | Subtotal |
+----+------------+----------+-------+----------+
| 1 | 1 | 5 | 100 | 500 |
| 2 | 1 | 3 | 50 | 150 |
| 3 | 2 | 5 | 60 | 300 |
+----+------------+----------+-------+----------+
Currently, I query for the Sale rows and then run another query for each to retrieve all of its Items after which, I initialize them as objects in my C# Program. My problem is that I frequently run into scenarios where I'm dealing with hundreds of sales at the same time. Because of this, I have to run hundreds of queries as well.
My question is, is there a more efficient way to query for all Sales and their Items or is this it? I thought of trying a query like
SELECT *
FROM items_table
WHERE FK_Sale_ID = '1' OR FK_Sale_ID = '2'
And then manually sorting which Item belonged to which sale for initialization but this query quickly got messy after dealing with more than a few sales. Any ideas?
You can use join clause to combine multiple tables data in your query:
SELECT *
FROM items_table
JOIN sales_table ON items_table.FK_Sale_ID = sales_table.ID
WHERE sales_table.Date = #somedate
With this query when #somedate = 11/20/2018 you basically will get this data in one go:
+----+------------+----+------------+----------+-------+----------+
| ID | Date | ID | FK_Sale_ID | Quantity | Price | Subtotal |
+----+------------+----+------------+----------+-------+----------+
| 1 | 11/20/2018 | 1 | 1 | 5 | 100 | 500 |
| 1 | 11/20/2018 | 2 | 1 | 3 | 50 | 150 |
+----+------------+----+------------+----------+-------+----------+
Use SELECT items_table.* to get all fields of items table only
I'm trying to do some SQL in C# along with an Access Database. I have two tables. Main Table and a Second Table:
MAIN TABLE
DoubleValue | | | |
----------------------------------------
1,40 | | | |
1,80 | | | |
2,00 | | | |
1,80 | | | |
1,60 | | | |
1,60 | | | |
----------------------------------------
SECOND TABLE
DoubleValue | Points | Weight |
-------------------------------
1,00 | 100 | 2 |
1,20 | 98 | 2 |
1,40 | 96 | 2 |
1,60 | 94 | 2 |
1,80 | 92 | 2 |
2,00 | 90 | 2 |
-------------------------------
I need to find all matching rows in "SECOND TABLE" based on the column "Double Value". Then for the rows that matches I want to get the value in columns "Points" and "Weight" as well as multplie those two columns and create a columns with the name "Sum" and add all three columns to the "MAIN TABLE":
MAIN TABLE - RESULT/OUTPUT
DoubleValue | Points | Weight | Sum |
-------------------------------------
1,40 | 96 | 2 | 192 |
1,80 | 92 | 2 | 184 |
2,00 | 90 | 2 | 180 |
1,80 | 92 | 2 | 184 |
1,60 | 94 | 2 | 188 |
1,60 | 94 | 2 | 188 |
-------------------------------------
The "MAIN TABLE" doesn't need to actually have the new columns "physically" inserted. I would very much prefer if they could just be displayed in the output very much like "SELECT Points * Weight AS Sum" would produce where "Sum" would be displayed but not actually inserted in the table. BUT OK, if it needs to actually be inserted then I will go with that.
How can this be done?
You are looking for a simpler INNER JOIN statement. Please notice that Sum is wrapped as it is a Reserved word in most SQL variants. Please try to avoid naming items with these words.
SELECT m.DoubleValue
, s.Points
, s.Weight
, [Sum] = s.Points * s.Weight
FROM MainTable AS m
INNER JOIN SecondTable AS s ON m.DoubleValue = s.DoubleValue
I want to create a table listing the comparison of each record in a table with every other record on basis of particular columns :
Example :
id | web | author | book | isbn | pub
----------------------------------------------------------------
1 | www.a.com | sam | sams book | 12345 | sams pub
2 | www.b.com | ram | rams book | 54321 | rams pub
3 | www.c.com | sam | rams book | 67891 | tams pub
4 | www.b.com | ram | gams book | 65644 | gams pub
5 | www.a.com | sam | sams book | 11111 | xyzs pub
6 | www.c.com | tam | tams book | 22222 | abcs pub
7 | www.c.com | tam | tams book | 33333 | pqrs pub
So I want to create table with the comparision result of each record with every other record
on columns web,author,book
Result Table should be : (result weight is addition of web+author+book weight)
sorceRow|destRow| web | author | book | result weight
--------------------------------------------------------
1 | 2 | 0 | 0 | 0 | 0
1 | 3 | 0 | 1 | 0 | 1
1 | 4 | 0 | 0 | 0 | 0
1 | 5 | 1 | 1 | 1 | 3
1 | 6 | 0 | 0 | 0 | 0
1 | 7 | 0 | 0 | 0 | 0
2 | 3 | 0 | 0 | 1 | 1
2 | 4 | 1 | 1 | 0 | 2
2 | 5 | 0 | 0 | 0 | 0
2 | 6 | 0 | 0 | 0 | 0
...
6 | 7 | 1 | 1 | 1 | 3
What is fastest way to get this result in SQL Server script as well as in C#?
You can do this with a non-equijoin and lots of comparisons. The following is the standard SQL method:
select t1.id as sourceRow, t2.id as destRow,
(case when t1.web = t2.web then 1 else 0 end) as Web,
(case when t1.Author = t2.Author then 1 else 0 end) as Author,
(case when t1.Book = t2.Book then 1 else 0 end) as Book,
(case when t1.ISBN = t2.ISBN then 1 else 0 end) as ISBN,
(case when t1.pub = t2.pub then 1 else 0 end) as pub
from table t1 join
table t2
on t1.id < t2.id;
Note that the comparisons would be a little more complicated if the columns could contain NULL values, but your sample data doesn't have any.
Basically CROSS JOIN of the table on itself will do that multiplication Cross Join.
As for comparison, it depends on the DB server you use - case or iif().
select
l.id as sourceRow,
r.id as destRow,
iif (r.web = l.web, 1, 0) as web,
iif (r.author = l.author, 1, 0) as author,
....
from T1 l
cross join T1 r
where l.id < r.id