Update value of row based on result from another table SQL Server - sql-server

Table One:
IdOne | IdTwo
------+------
32423 | 435
2343 | 345
2344 | 45
Table one gets both idOne and idTwo from two other tables. IdOne stores the ids of a venue. The table from where it gets its id from stores whether or not this venue is active. Users can deactivate a venue and activate it. There are some duplicates that point to the same venue and I would like to set the value of the duplicates(in table one) to the value of the active venue I get from the table that stores this information.
I tried sub querying, correlated querying and I have not gotten far. Any help will be appreciated greatly.
EDIT:
Sorry for the badly worded question. I was a bit frantic. Here is the correct question.
I have two tables. A many to many table associating performances to venues. And a venues table.
The many to many table has the layout:
performance_venue
(
performanceId,
venueId
)
The venue table has the layout:
venue
(
uniqueId,
venueTypeId,
active
)
They are related by venue.uniqueId = performance_venue.venueId. There are instances in performance_venue that refer to venue instances that have an active value of 0. These venues that have an active value of 0 have an updated venue instance in which the active value is 1 and have the same venueTypeId. So, what I would like to do is to update all the performance_venue instances to refer to the venue instances that have an active value of 1 if they currently are referring to a venue instance that has an active value of 0.
Here is an example.
performance_venue
performanceId | venueId
---------------+--------
1 | 1
2 | 2
3 | 3
venue
uniqueId | venueTypeId | active
---------+-------------+-------
1 | 1 | 0
2 | 1 | 1
3 | 2 | 1
Expected result after update
performance_venue
performanceId | venueId
---------------+---------
1 | 2
2 | 2
3 | 3

The solutoin involves constructing a table that has both the active and non-active venue in the same row. Then you just set the performance_venue venueId to the active uniqueId.
UPDATE pv
SET pv.venueId = active_uniqueId
FROM venue v
JOIN performance_venue pv ON pv.venueId = v.uniqueId
JOIN (
SELECT venueTypeId, uniqueId as active_uniqueId
FROM venue
) v_active ON v.venueTypeId = v_active.venueTypeId
WHERE v.active = 0 AND v.venueTypeId in (
SELECT venueTypeId
FROM venue v_sub
WHERE active = 1 and v.venueTypeId = v_sub.venueTypeId
)
AND v.uniqueId != active_uniqueId

UPDATE t1
SET t1.IdTwo = (SELECT top 1 t2.IdTwo FROM TableTwo t2 WHERE t2.IdOne = t1.IdOne AND t2.Active = 1)
FROM TableOne t1

Related

How To Avoid TempTable in Union All when queries contain DIFFERENT order by and inner join?

What i am trying to do is always sending Product with 0 quantity to the end of an already sorted temp Table without losing current sorting (as i described in the following question How to send Zero Qty Products to the end of a PagedList<Products>?)
I have one Sorted temptable which is filled (it is sorted by what user has selected like Alphabetic , by Price or by Newer product,sorting is based identity id) :
CREATE TABLE #DisplayOrderTmp
(
[Id] int IDENTITY (1, 1) NOT NULL,
[ProductId] int NOT NULL
)
sorted #DisplayOrderTmp :
+------------+---------------+
| id | ProductId |
+------------+---------------+
| 1 | 66873 | // Qty is 0
| 2 | 70735 | // Qty is not 0
| 3 | 17121 | // Qty is not 0
| 4 | 48512 | // Qty is not 0
| 5 | 51213 | // Qty is 0
+------------+---------------+
I want pass this data to web-page, but before it i need to send product with zero quantity to the end of this list without loosing current Sorting by)
My returned data should be like this (sorting doesn't changed just 0 quantity products went to the end of list by their order):
CREATE TABLE #DisplayOrderTmp4
(
[Id] int IDENTITY (1, 1) NOT NULL,
[ProductId] int NOT NULL
)
+------------+---------------+
| id | ProductId |
+------------+---------------+
| 1 | 70735 |
| 2 | 17121 |
| 3 | 48512 |
| 4 | 66873 |
| 5 | 51213 |
+------------+---------------+
P.S: Its My product Table which i have to inner join with tmptable to find qty of products.
Product Table is like this :
+------------+---------------+------------------+
| id | stockqty | DisableBuyButton |
+------------+---------------+------------------+
| 17121 | 1 | 0 |
| 48512 | 27 | 0 |
| 51213 | 0 | 1 |
| 66873 | 0 | 1 |
| 70735 | 11 | 0 |
+------------+---------------+------------------+
What i have tried so far is this : (it works with delay and has performance issue i almost have 30k products)
INSERT INTO #DisplayOrderTmp2 ([ProductId])
SELECT p2.ProductId
FROM #DisplayOrderTmp p2 with (NOLOCK) // it's already sorted table
INNER JOIN Product prd with (NOLOCK)
ON p2.ProductId=prd.Id
and prd.DisableBuyButton=0 // to find product with qty more than 0
group by p2.ProductId order by min(p2.Id) // to save current ordering
INSERT INTO #DisplayOrderTmp3 ([ProductId])
SELECT p2.ProductId
FROM #DisplayOrderTmp p2 with (NOLOCK) //it's already sorted table
INNER JOIN Product prd with (NOLOCK)
ON p2.ProductId=prd.Id
and prd.DisableBuyButton=1 // to find product with qty equal to 0
group by p2.ProductId order by min(p2.Id) // to save current ordering
INSERT INTO #DisplayOrderTmp4 ([ProductId]) // finally Union All this two data
SELECT p2.ProductId FROM
#DisplayOrderTmp2 p2 with (NOLOCK) // More than 0 qty products with saved ordering
UNION ALL
SELECT p2.ProductId FROM
#DisplayOrderTmp3 p2 with (NOLOCK) // 0 qty products with saved ordering
Is there any way To Avoid creating TempTable in this query? send 0
quantity products of first temptable to the end of data-list without
creating three other tempTable , without loosing current ordering based by Identity ID.
My query has performance problem.
I have to say again that the temptable has a identity insert ID column and it is sorted based sorting type which user passed to Stored-Procedure.
Thank You All :)
Make sure the temp table has an index or primary key with Id as the leading column. This will help avoid sort operators in the plan for the ordering:
CREATE TABLE #DisplayOrderTmp
(
[Id] int NOT NULL,
[ProductId] int NOT NULL
,PRIMARY KEY CLUSTERED(Id)
);
With that index, you should be able to get the result without additional temp tables with reasonable efficiency using a UNION ALL query, assuming ProductID is the Product table primary key:
WITH products AS (
SELECT p2.Id, p2.ProductId, prd.stockqty, 1 AS seq
FROM #DisplayOrderTmp p2
JOIN Product prd
ON p2.ProductId=prd.Id
WHERE prd.stockqty > 0
UNION ALL
SELECT p2.Id, p2.ProductId, prd.stockqty, 2 AS seq
FROM #DisplayOrderTmp p2
JOIN Product prd
ON p2.ProductId=prd.Id
WHERE prd.stockqty = 0
)
SELECT ProductId
FROM products
ORDER BY seq, Id;
You mentioned in comments that you ultimately want a paginated result. This can be done in T-SQL by adding OFFSET and FETCH to the ORDER BY clause as below. However, be aware that pagination over a large result set will become progressively slower the further into the result one queries.
WITH products AS (
SELECT p2.Id, p2.ProductId, prd.stockqty, 1 AS seq
FROM #DisplayOrderTmp p2
JOIN Product prd
ON p2.ProductId=prd.Id
WHERE prd.stockqty > 0
UNION ALL
SELECT p2.Id, p2.ProductId, prd.stockqty, 2 AS seq
FROM #DisplayOrderTmp p2
JOIN Product prd
ON p2.ProductId=prd.Id
WHERE prd.stockqty = 0
)
SELECT ProductId
FROM products
ORDER BY seq, Id
OFFSET #PageSize * (#PageNumber - 1) ROWS
FETCH NEXT #PageSize ROWS ONLY;
You could use ORDER BY without using UNION ALL:
SELECT p2.ProductId
FROM #DisplayOrderTmp p2
JOIN Product prd
ON p2.ProductId=prd.Id
ORDER BY prd.DisableBuyButton, p2.id;
DisableBuyButton = 0 - qnt > 0
DisableBuyButton = 1 - qnt = 0
Seems it only needs an extra something in the order by.
An IIF or CASE can be used to give a priority to the sorting.
SELECT tmp.ProductId
FROM #DisplayOrderTmp tmp
JOIN Product prd
ON prd.Id = tmp.ProductId
AND prd.DisableBuyButton IN (0,1)
ORDER BY IIF(prd.DisableBuyButton=0,1,2), tmp.id;

sql server max record value grouped by id tuple from separate table

There are many questions already on SO asking how to do a general max value with group by some id. However my particular case is somewhat different.
What I have is a record with a value that links to any unknown number of profiles associated (as a "team") with that record. For simplicity in the example each team has 2 profiles but the real example could have any size.
From these records I'm trying to create a leaderboard to show the max record from each unique team formation and should only show one result even if the team scored the same max value more than once.
In this example the unique teams are (1, 2) and (2, 3).
EDIT: Unique team formation means that the leaderboard should consider all records with profiles (1, 2) to be the same unique formation of a team (as a unique id if that helps) even though the same team may have been formed multiple times for different records.
In this example team (1,2) has a duplicate max record value of 1 which should ignore the duplicate.
Lets say was have 3 users:
Table: profile
profileId | name
1 | John
2 | James
3 | Mark
Then lets say there are currently the following records:
Table: record
recordId | value
1 | 1
2 | 1
3 | 2
4 | 3
And finally each record is made of the following teams described by their members:
Table: member
recordId | profileId
1 | 1
1 | 2
2 | 1
2 | 2
3 | 2
3 | 3
4 | 3
4 | 2
The final output should look like:
recordId | profileId1 | profileId2 | value
4 | 2 | 3 | 4
1 (or 2) | 1 | 2 | 0
So far I've seem something like this to do it if the group id was part of the record:
SELECT *
FROM (SELECT *,
ROW_NUMBER() OVER (PARTITION BY profileId ORDER BY value DESC) N
FROM record
) M WHERE N = 1
And this to actually get the unique tuples:
select max(r.value) as value, p1.profileId as p1, p2.profileId as p2
from record r
inner join profile p1 on p1.recordId = r.id
inner join profile p1 on p2.recordId = r.id
where p1.profileId < p2.profileId
group by p2.profileId, p2.profileId
However, I don't know how to piece it together to get the max record for each tuple of profiles.
Also, the second query isn't very scalable for any unknown number of profiles and if there is a way to do it without self joining based on the number of profiles that would be a bonus!
If someone can help me build the right query for SQL Server that would be awesome.
Thanks!
After a bunch more research and trial and error I came to the following answer that solves my problem. This query will give the top scores from each team for a 2 person team leaderboard:
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY m1.profileId, m2.profileId, m3.profileId ORDER BY r.value DESC) N,
m1.profileId AS m1, m2.profileId AS m2, m3.profileId AS m3, r.value, r.id
FROM record r
INNER JOIN member m1 ON m1.recordId = r.id
INNER JOIN member m2 ON m2.recordId = r.id
WHERE m1.profileId < m2.profileId
) R
where N = 1
ORDER BY value DESC;
It works by running a partition to rank all the records by team and then plucks only the record ranked 1. The where m1.profileId < m2.profileId ensures that only 1 permutation of the team is used in the results.

SSIS - transform 2 records from table A to 1 record in table B

I have following data in employee Table A:
ID | emp | City_Type | City
1 | 101 | Z | Tokyo
2 | 101 | Y | New York
City_Type can either be Y or Z. Y being the city this person was born in, Z is the city he/she is living now.
I need to put these together in a table 'B' which look like the folowing:
ID | emp | Current_City | Birth_City
So in the end, Table B must be filled like this:
ID | emp | Current_City | Birth_City
1 | 101 | Tokyo | New York
(in some cases, one of the 2 can be empty/null)
Any suggestions on how to do this? I haven't been able to found much information on this myself.
I did this exercise (using sql-server) with PIVOT TABLE:
select emp, Z 'Current_City' , Y 'Birth_City' from
(
select emp,City_Type, City from TABLE__A
) x
pivot
(
max(City) FOR City_Type in (Z,Y)
) AS PivotTable
below the result achieved, with an example of a NULL value for the field Current_City
emp Current_City Birth_City
101 Tokyo New York
102 NULL London
I omitted ID, it is not clear from the request if and what needs to be added ( minimum, maximum on emp , or a new calculated or due to the INSERT in TABLE__B)
This previous query can be used to insert into TABLE__B
INSERT INTO [TABLE__B]
([emp]
,[Current_City]
,[Birth_City])
...
First create your TableB and populate [Current_City] and [Birth_City] with nulls, but make sure [emp] is there and it has all the employees you intend to modify.
Then run this SQL modified to fit your database / schema / table names / etc:
update TableB
set Current_City = (select City
from TableA
where TableA.City_Type ='Z'
and TableA.emp = TableB.emp),
Birth_City = (select City
from TableA
where TableA.City_Type ='Y'
and TableA.emp = TableB.emp)
One way would be to use the PIVOT transformation.

sql - count column depending on whether parents or children

I'm looking to get the sum of a column in certain conditions i.e. depending on whether a column is a parent with subtasks or just a parent with no subtasks. If a task is a parent with subtasks I only want the sum of the "complete" column of the subtasks. If a task is only a parent I would only want the sum of the complete column of the parent. - but only if the tasks(parent or children) are all related to the same user.
E.g. In the following example table:
UserID | Parent_TaskID | TaskID | Complete
------ | ------------- | ------ | --------
435 | 149329 | 161280 | 1
435 | 149330 | 210717 | 2
435 | 149330 | 228100 | 3
435 | 156991 | 149330 | 1
169 | 458764 | 546540 | 2
169 | 456842 | 546541 | 2
169 | 456842 | 458764 | 0
TaskID 149330 is a parent with children 210717 & 228100 so the count for that column for 149330 is 5, i.e. ignoring the complete column for the parent. 161280 is a parent so only would return 0 for that. 546540 is a subtask of 458764, so 458764 would have a sum of 2.
So I think what I the result of this should look like:
TaskID | Sum_complete
------- | ------------
161280 | 1
149330 | 5
546541 | 2
458764 | 2
Any ideas how this could be done?
I've created a table at SqlFiddle http://sqlfiddle.com/#!2/8295f
Thanks,
I can get the parents by using the following:
select t.taskID, t.Parent_taskID, t.userID, t.complete
from task t
where t.Parent_taskID not in (
select tp.taskID
from task tp
where tp.userID = t.userID
)
Based on your description, I think you are asking for the following (using the sample data from the question):
EDIT: Replaced query to eliminate children from the resultset.
declare #Task as Table
( UserId varchar(6), Parent_TaskId varchar(13), TaskId varchar(6), Complete integer );
INSERT INTO #Task ( UserId, Parent_TaskId, TaskId, Complete ) VALUES
('435', '149329', '161280', 1 ),
('435', '149330', '210717', 2 ),
('435', '149330', '228100', 3 ),
('435', '156991', '149330', 1 ),
('169', '458764', '546540', 2 ),
('169', '456842', '546541', 2 ),
('169', '456842', '458764', 0 );
; with QualifiedTasks as (
select UserId, TaskId, Parent_TaskId, Complete,
case when exists ( select 42 from #Task where Parent_TaskId = O.TaskId ) then 1 else 0 end as Parent,
case when exists ( select 42 from #Task where O.Parent_TaskId = TaskId ) then 1 else 0 end as Child, -- Unused, but here for completeness.
case when not exists ( select 42 from #Task where Parent_TaskId = O.TaskId or O.Parent_TaskId = TaskId ) then 1 else 0 end as Loner
from #Task as O )
select L.TaskID, Sum( Coalesce( R.Complete, L.Complete ) ) as Sum_Complete
from QualifiedTasks as L left outer join
QualifiedTasks as R on R.Parent_TaskId = L.TaskId and R.UserId = L.UserId
where L.Parent = 1 or L.Loner = 1
group by L.TaskId;
A couple of obvious optimizations spring to mind. The Parent, Child and Loner columns are redundant, any of the three can be eliminated. Parent can be determined in the summary query by Max( R.Complete ) is not NULL. A clever trick for Child or Loner escapes me at the moment.
This should work (though the data you posted on your question is different than the one on the fiddle):
SELECT ISNULL(B.taskId,A.TaskId) TaskId, SUM(A.Complete) Complete
FROM Task A
LEFT JOIN Task B
ON A.Parent_TaskID = B.taskId
GROUP BY ISNULL(B.taskId,A.TaskId)

SQL Server problems when using ODER BY and DISTINCT together

I got two problems, the first problem is my two COUNTS that I start with. GroupID is a string that keep products together (Name_Year together), same product but different size.
If I have three reviews in tblReview and they all have the same GroupID I want to return 3. My problem is that if I have three Products with different ProductID but same GroupID and I add three Review to that GroupID I got 9 returns (3*3). If I only have one Product With the same GroupID and three Reviews it works (1*3=3 returns)
The Second problem is that if I have the ORDER BY CASE Price I have to add GROUP BY Price as well and then I don't get the DISTINCT effect that I want. And that is to just show products that have unique GroupID.
Here's the query, hope somebody can help me with this.
ALTER PROCEDURE GetFilterdProducts
#CategoryID INT, #ColumnName varchar(100)
AS
SELECT COUNT(tblReview.GroupID) AS ReviewCount,
COUNT(tblComment.GroupID) AS CommentCount,
Product.ProductID,
Product.Name,
Product.Year,
Product.Price,
Product.BrandID,
Product.GroupID,
AVG(tblReview.Grade) AS Grade
FROM Product LEFT JOIN
tblComment ON Product.GroupID = tblComment.GroupID LEFT JOIN
tblReview ON Product.GroupID = tblReview.GroupID
WHERE (Product.CategoryID = #CategoryID)
GROUP BY Product.ProductID, Product.BrandID, Product.GroupID, Product.Name, Product.Year, Product.Price
HAVING COUNT(distinct Product.GroupID) = 1
ORDER BY
CASE
WHEN #ColumnName='Name' THEN Name
WHEN #ColumnName='Year' THEN Year
WHEN #ColumnName='Price' THEN Price
END
My tabels:
Product:
ProductID, Name, Year, Price, BrandID, GroupID
tblReview:
ReviewID, Description, Grade, ProductID, GroupID
tblComment:
CommentID, Description, ProductID, GroupID
I think that my problem is that if I have three GroupID with the same name, ex Nike_2010 in Product and I have three Reviews in tblReview that counts the first row in Products that contain Nike_2010 counts how many reviews in tblReview with the same GroupID, Nike_2010 and then the second row in Product that contains Nike_2010 and then do the same count again and again, that results to 9 rows. How do I avoid that?
For starters, because you're joining on multiple tables, you're going to end up with the cross product of all of them as a result. Your counts will then return the total count of rows containing data in that column. Consider the following example:
- PRODUCTS - -- COMMENTS -- --- REVIEWS ---
Key | Name Key | Comment Key | Review
1 | A 1 | Foo 1 | Great
2 | B 1 | Bar 1 | Wonderful
The query
SELECT PRODUCTS.Key, PRODUCTS.Name, COMMENTS.Comment, REVIEWS.Review
FROM PRODUCTS
LEFT OUTER JOIN COMMENTS ON PRODUCTS.KEY = COMMENTS.KEY
LEFT OUTER JOIN REVIEWS ON PRODUCTS.KEY = REVIEWS.KEY
will result in the following data:
Key | Name | Comment | Review
1 | A | Foo | Great
1 | A | Foo | Wonderful
1 | A | Bar | Great
1 | A | Bar | Wonderful
2 | B | NULL | NULL
Thus, counting in this format
SELECT PRODUCTS.Key, PRODUCTS.Name, COUNT(COMMENTS.Comment), COUNT(REVIEWS.Review)
FROM PRODUCTS
LEFT OUTER JOIN COMMENTS ON PRODUCTS.KEY = COMMENTS.KEY
LEFT OUTER JOIN REVIEWS ON PRODUCTS.KEY = REVIEWS.KEY
GROUP BY PRODUCTS.Key, PRODUCTS.Name
will give you
Key | Name | Count1 | Count2
1 | A | 4 | 4
2 | B | 0 | 0
because it's counting each row in the table produced by the join!
Instead, you want to count each table separately in a subquery before joining it back like the following:
SELECT PRODUCTS.Key, PRODUCTS.Name, ISNULL(CommentCount.NumComments, 0),
ISNULL(ReviewCount.NumReviews, 0)
FROM PRODUCTS
LEFT OUTER JOIN (SELECT Key, COUNT(*) as NumComments
FROM COMMENTS
GROUP BY Key) CommentCount on PRODUCTS.Key = CommentCount.Key
LEFT OUTER JOIN (SELECT Key, COUNT(*) as NumReviews
FROM REVIEWS
GROUP BY Key) ReviewCount on PRODUCTS.Key = ReviewCount.Key
which will produce the following
Key | Name | NumComments | NumReviews
1 | A | 2 | 2
2 | B | 0 | 0
As for the "DISTINCT effect" you refer to, I'm not exactly sure I follow. Could you elaborate a bit?
About second problem - cannot you group by same CASE statement? You shouldn't have Price field in results list then though.

Resources