SQL Select with Priority - sql

I need to select top 1 most valid discount for a given FriendId.
I have the following tables:
DiscountTable - describes different discount types
DiscountId, Percent, Type, Rank
1 , 20 , Friend, 2
2 , 10 , Overwrite, 1
Then I have another two tables (both list FriendIds)
Friends
101
102
103
Overwrites
101
105
I have to select top 1 most valid discount for a given FriendId. So for the above data this would be sample output
Id = 101 => gets "Overwrite" discount (higher rank)
Id = 102 => gets "Friend" discount (only in friends table)
Id = 103 => gets "Friend" discount (only in friends table)
Id = 105 => gets "Overwrite" discount
Id = 106 => gets NO discount as it does not exist in neither Friend and overwrite tables
INPUT => SINGLE friendId (int).
OUTPUT => Single DISCOUNT Record (DiscountId, Percent, Type)
Overwrites and Friend tables are the same. They only hold list of Ids (single column)

Having multiple tables of identical structure is usually bad practice, a single table with ID and Type would suffice, you could then use it in a JOIN to your DiscountTable:
;WITH cte AS (SELECT ID,[Type] = 'Friend'
FROM Friends
UNION ALL
SELECT ID,[Type] = 'Overwrite'
FROM Overwrites
)
SELECT TOP 1 a.[Type]
FROM cte a
JOIN DiscountTable DT
ON a.[Type] = DT.[Type]
WHERE ID = '105'
ORDER BY [Rank]
Note, non-existent ID values will not return.

This will get you all the FriendIds and the associate discount of the highest rank. It's an older hack that doesn't require using top or row numbering.
select
elig.FriendId,
min(Rank * 10000 + DiscountId) % 10000 as DiscountId
min(Rank * 10000 + Percent) % 10000 as Percent,
from
DiscountTable as dt
inner join (
select FriendId, 'Friend' as Type from Friends union all
select FriendId, 'Overwrite' from Overwrites
) as elig /* for eligible? */
on elig.Type = dt.Type
group by
elig.FriendId

create table discounts (id int, percent1 int, type1 varchar(12), rank1 int)
insert into discounts
values (1 , 20 , 'Friend', 2),
(2 , 10 , 'Overwrite', 1)
create table friends (friendid int)
insert into friends values (101),(102), (103)
create table overwrites (overwriteid int)
insert into overwrites values (101),(105)
select ids, isnull(percent1,0) as discount from (
select case when friendid IS null and overwriteid is null then 'no discount'
when friendid is null and overwriteid is not null then 'overwrite'
when friendid is not null and overwriteid is null then 'friend'
when friendid is not null and overwriteid is not null then (select top 1 TYPE1 from discounts order by rank1 desc)
else '' end category
,ids
from tcase left outer join friends
on tcase.ids = friends.friendid
left join overwrites
on tcase.ids = overwrites.overwriteid
) category1 left join discounts
on category1.category=discounts.type1

Related

Get child hierarchy for a subset of users

I have a customer table where there can be a hierarchy of parent-child records.
The customer table has a structure like,
customer {
id integer,
user_id integer,
parent_id integer
}
Given an user_id I know how to fetch all child users in hierachy that belong to the given user_id. For example for user_id value of 100,
WITH RECURSIVE subordinates AS (
SELECT c1.*
FROM customer c1
WHERE c1.user_id = 100
UNION
SELECT c2.*
FROM customer c2
INNER JOIN subordinates s
ON s.id = c2.parent_id)
SELECT * FROM subordinates;
But I would like to restrict the output of above query to a subset of users. So the first argument to the above query will be a parent user_id (let's call it parent_user) and the second argument will be a list of user_id (let's call it child_users).
What I would like to do is,
Iterate over child_users
Check if child_user in child_users is a child of parent_user
If yes, fetch entire child hierachy of child_user
Can above 3 steps be achieved in a single sql query. Plese suggest.
EDIT
The sample table data can be,
id user_id parent_id
-----------------------
1 100 null
2 101 100
3 102 101
4 103 102
5 104 100
6 105 null
So given above data in the table, for input values of parent_user = 100 and child_users = [101, 105] I am expetcting to retrieve following output,
id user_id parent_id
-----------------------
2 101 100
3 102 101
4 103 102
The record with id = 5 should't be part of the output even though it has parent_id = 100 since user_id 104 is not in the child_users list.
Please see the answers below and let me know if that works
declare #Customer table ( Id int,userID int,parentID int)
insert #Customer (Id, userID, parentID)
select 1,100,null union all <br/>
select 1,101,100 union all <br/>
select 1,102,101 union all <br/>
select 1,103,102 union all <br/>
select 1,104,100 union all <br/>
select 1,105,null <br/>
;with cte_emp as <br/>
( <br/>
select ID,userID,parentID
from #Customer
where parentID=100 and userID in (101,105) <br/>
union all <br/>
select e.ID,e.userID,e.parentID
from #Customer e join cte_emp cte
on cte.userid=e.parentID <br/>
) <br/>
select * from cte_emp <br/>
If I follow you correctly, you want to filter on the immediate parent of the starting node. You can just put that condition in the anchor of the CTE:
with recursive subordinates as (
select c.*
from customer
where user_id = any($1) and parent_id = $2
union all
select c.*
from customer c
inner join subordinates s on s.id = c.parent_id
)
select * from subordinates;
Note that I changed the query to accept to input parameters:
$1 is an array of child ids
$2 is the id of the parent that is used to filter

Select rows base on Subset

I've a scenario where I need to write sql query base on result of other query.
Consider the table data:
id attribute
1 a
1 b
2 a
3 a
3 b
3 c
I want to write query to select id base on attribute set.
I mean first I need to check attribute of id 1 using this query:
select attribute from table where id = 1
then base on this result I need to select subset of attribute. like in our case 1(a,b) is the subset of 3(a,b,c). My query should return 3 on that case.
And if I want to check base on 2(a) which is the subset of 1(a,b) and 3(a,b,c), it should return 1 and 3.
I hope, it's understandable. :)
You could use this query.
Logic is simple: If there isn't any item in A and isn't in B --> A is subset of B.
DECLARE #SampleData AS TABLE
(
Id int, attribute varchar(5)
)
INSERT INTO #SampleData
VALUES (1,'a'), (1,'b'),
(2,'a'),
(3,'a'),(3,'b'),(3,'c')
DECLARE #FilterId int = 1
;WITH temp AS
(
SELECT DISTINCT sd.Id FROM #SampleData sd
)
SELECT * FROM temp t
WHERE t.Id <> #FilterId
AND NOT EXISTS (
SELECT sd2.attribute FROM #SampleData sd2
WHERE sd2.Id = #FilterId
AND NOT EXISTS (SELECT * FROM #SampleData sd WHERE sd.Id = t.Id AND sd.attribute = sd2.attribute)
)
Demo link: Rextester
I would compose a query for that in three steps: first I'd get the attributes of the desired id, and this is the query you wrote
select attribute from table where id = 1
Then I would get the number of attributes for the required id
select count(distinct attribute) from table where id = 1
Finally I would use the above results as filters
select id
from table
where id <> 1 and
attribute in (
select attribute from table where id = 1 /* Step 1 */
)
group by id
having count(distinct attribute) = (
select count(distinct attribute) from table where id = 1 /* Step 2 */
)
This will get you all the id's that have a number of attributes among those of the initially provided id equal to the number the initial id has.

SQL-Get highest record from group at another table

I have read several answers to related questions but none of them can be applied to this case.
I have a table TableA where several groups are listed, with their score:
GROUP|SCORE
Blue | 0
Green| 0
Red | 0
Orange| 0
On another table TableB, I have the parts of each group and their individual score (status), which can have three different values:
- G (Good)
- A (Average)
- B (Bad)
So tableB is:
GROUP|PART|STATUS
Blue | 3H2| A
Blue | 4NQ| G
Blue | W9X| A
Green| 65D| G
Red | 73F| B
Red | 91G| A
I need to Update the score on TableA in the following way:
If the best status between the parts of the group is G, group score is 3
If the best status between the parts of the group is A, group score is 2
If the best status between the parts of the group is B, group score is 1
I have been a couple of days going around this and I can't find a solution. Thank you guys. Btw, I am using Access 2013.
As I have already mentioned in the comments: Don't store the score redundantly; it is implicit in tableB. And to get your database straight introduce a status table:
STATUS DESCRIPTION SCORE
G Good 3
A Avarage 2
B Bad 1
If you want to select the score for each color group use this query for instance:
select b.colorgroup, max(s.score) as maxscore
from tableb as b
join status as s on s.status = b.status
group by b.colorgroup;
Two alternative ways to write the same query:
select
colorgroup,
(
select max(score)
from status as s
where s.status = b.status
) as maxscore
from tableb as b;
and
select b.colorgroup, s.maxscore
from tableb as b
join
(
select status, max(score) as maxscore
from status
group by status
) as s on s.status = b.status;
(BTW: I called your group colorgroup because GROUP is a reserved name in SQL.)
UPDATE You say you cannot add a table to the database. So you must evaluate the score in the query itself unfortunately. In standard SQL you would use CASE WHEN, which MS Access doesn't feature. MS Access provides IIF instead:
select
colorgroup,
max(iif(status = 'G', 3, iif(status = 'A', 2, 1))) as maxscore
from tableb
group by colorgroup;
If you even must use the column in tableA and store redundantly, use:
update tablea as a
set score =
(
select
max(iif(status = 'G', 3, iif(status = 'A', 2, 1))) as maxscore
from tableb as b
where b.colorgroup = a.colorgroup
);
In SQL-Server you could do in following:
QUERY
update a
set a.SCORE = MaxSTATUS
from #a a
join (select GROUP_, MAX(case when b.STATUS_ = 'G' then 3
when b.STATUS_ = 'A' then 2
when b.STATUS_ = 'B' then 1
end) MaxSTATUS
from #b b
group by GROUP_
) b ON a.GROUP_ = b.GROUP_
select * from #a
SAMPLE DATA
CREATE TABLE #a
(
GROUP_ NVARCHAR(60),
SCORE INT
)
INSERT INTO #a VALUES
('Blue',0)
,('Green',0)
,('Red',0)
,('Orange',0)
CREATE TABLE #b
(
GROUP_ NVARCHAR(60),
PART NVARCHAR(60),
STATUS_ NVARCHAR(60),
)
INSERT INTO #b VALUES
('Blue','3H2','A')
,('Blue','4NQ','G')
,('Blue','W9X','A')
,('Green','65D','G')
,('Red','73F','B')
,('Red','91G','A')
OUPUT
GROUP_ SCORE
Blue 3
Green 3
Red 2
Orange 0

edit and Update records using reference id

i have table with multiple records in a field name Comments... with my aspx code the data in comments column gets inserted in three rows with different requirementcommentid but the field comment will remain same
to retrieve distinct i used this query
SELECT distinct (
select top 1 requirementcommentid
from Requirementcomment
where requirementcomment=rc.requirementcomment
and fcr.SectionID in(
SELECT sectionid
FROM [dbo].udfGetSectionID_allComYear(2151)
)
AND fcr.FirmID = 20057
),
rc.IsRejected,
fcr.SectionID,
rc.UserID,
rc.RequirementComment,
convert(varchar(25), dateadd(hour, -5, rc.InsertDate),101) as InsertDate,
Department.DeptName,
FirmUser.DepartmentID,
rc.FirmComplianceYearID
FROM RequirementComment rc
INNER JOIN FirmComplianceRequirement fcr ON fcr.FirmComplianceRequirementID = rc.FirmComplianceRequirementID
INNER JOIN FirmUser ON FirmUser.FirmUserID =rc.UserID
INNER JOIN Department ON Department.DeptID = FirmUser.DepartmentID WHERE rc.IsRejected = 1
AND fcr.SectionID in(SELECT sectionid FROM [dbo].udfGetSectionID_allComYear (2151))
AND fcr.FirmID = 20057 AND rc.RequirementComment!=''
if i want to edit this distinct comment and update it.how can i do this... as only one comment row get edited remaining two rows value in field comment remain the same...!
i want remaining data to be updated automatically if i clicked on edit and updated only single record
If you can not solve this with a procedure when storing, or in .NET, consider to use a trigger. I have made a generic example, since your example code is a bit complex :)
CREATE TABLE TMP_TriggerTable
(
ID INT IDENTITY(1,1) PRIMARY KEY
, ID2 INT NOT NULL
, Comment VARCHAR(255) NOT NULL
)
GO
INSERT INTO TMP_TriggerTable
SELECT 1, 'asd'
UNION ALL
SELECT 1, 'asd'
UNION ALL
SELECT 1, 'asd'
UNION ALL
SELECT 2, 'asd'
UNION ALL
SELECT 2, 'asd'
UNION ALL
SELECT 2, 'asd'
GO
CREATE TRIGGER TRG_TMP_TriggerTable ON TMP_TriggerTable
AFTER UPDATE
AS
BEGIN
WITH InsertedIDPriority AS
(
--Handle if more than one related comment was updated
SELECT Prio = ROW_NUMBER() OVER (PARTITION BY ID2 ORDER BY ID)
, ID
, ID2
, Comment
FROM INSERTED
)
UPDATE t SET Comment = i.Comment FROM TMP_TriggerTable t
JOIN InsertedIDPriority i ON
t.ID2 = i.ID2 --Select all related comments
AND t.ID != i.ID2 --No need to update main column two times
AND i.Prio = 1 --Handle if more than one related comment was updated
END
GO
UPDATE TMP_TriggerTable SET Comment = 'asd2' WHERE ID = 1
/*
SELECT * FROM TMP_TriggerTable
--Returns--
ID ID2 Comment
1 1 asd2
2 1 asd2
3 1 asd2
4 2 asd
5 2 asd
6 2 asd
*/

sql select a field into 2 columns

I am trying to run below 2 queries on the same table and hoping to get results in 2 different columns.
Query 1: select ID as M from table where field = 1
returns:
1
2
3
Query 2: select ID as N from table where field = 2
returns:
4
5
6
My goal is to get
Column1 - Column2
-----------------
1 4
2 5
3 6
Any suggestions? I am using SQL Server 2008 R2
Thanks
There has to be a primary key to foreign key relationship to JOIN data between two tables.
That is the idea about relational algebra and normalization. Otherwise, the correlation of the data is meaningless.
http://en.wikipedia.org/wiki/Database_normalization
The CROSS JOIN will give you all possibilities. (1,4), (1,5), (1, 6) ... (3,6). I do not think that is what you want.
You can always use a ROW_NUMBER() OVER () function to generate a surrogate key in both tables. Order the data the way you want inside the OVER () clause. However, this is still not in any Normal form.
In short. Why do this?
Quick test database. Stores products from sporting goods and home goods using non-normal form.
The results of the SELECT do not mean anything.
-- Just play
use tempdb;
go
-- Drop table
if object_id('abnormal_form') > 0
drop table abnormal_form
go
-- Create table
create table abnormal_form
(
Id int,
Category int,
Name varchar(50)
);
-- Load store products
insert into abnormal_form values
(1, 1, 'Bike'),
(2, 1, 'Bat'),
(3, 1, 'Ball'),
(4, 2, 'Pot'),
(5, 2, 'Pan'),
(6, 2, 'Spoon');
-- Sporting Goods
select * from abnormal_form where Category = 1
-- Home Goods
select * from abnormal_form where Category = 2
-- Does not mean anything to me
select Id1, Id2 from
(select ROW_NUMBER () OVER (ORDER BY ID) AS Rid1, Id as Id1
from abnormal_form where Category = 1) as s
join
(select ROW_NUMBER () OVER (ORDER BY ID) AS Rid2, Id as Id2
from abnormal_form where Category = 2) as h
on s.Rid1 = h.Rid2
We definitely need more information from the user.