SQL to fetch data using column values from one table as columns of another table - sql

I have these two tables:
Org_Extra_Attr
org_id attr_name attr_path
1 desk_name str1
1 citizen bool1
2 perm_user bool1
3 skype_id str1
3 twitter str2
User_Attr_Values
org_id user_id str1 str2 str3 str4 bool1 bool2 bool3 bool4
1 1 b1d07 null null null 1 null null null
1 2 b2d01 null null null 0 null null null
2 3 null null null null 1 null null null
2 4 null null null null 1 null null null
3 5 sam_sky sam_twt null null null null null null
3 6 tom_sky tom_twt null null null null null null
So, the thing here is each org.can define max.of 4 extra attributes of type String and Boolean each, the Org_Extra_Attr table is like meta-data. For example org_id 1 has defined desk_name which will be str1's value for its users, whereas org_id 3 has skype_id which will be str1's value for its users.
This might be a bad design, but for now, I need to get users attribute names and values for a given org_id. Like for org_id = 1, I need a SQL query result(not a third table) like below:
user_id attr_name val
1 desk_name b1d07
1 citizen 1
2 desk_name b2d01
2 citizen 0
For org_id = 3
user_id attr_name val
5 skype_id sam_sky
5 twitter sam_twt
6 skype_id tom_sky
6 twitter tom_twt

Something like this
select
t2.user_id,
t1.attr_name,
CASE
WHEN t1.attr_path='str1' then t2.str1
WHEN t1.attr_path='str2' then t2.str2
WHEN t1.attr_path='str3' then t2.str3
WHEN t1.attr_path='str4' then t2.str4
WHEN t1.attr_path='bool1' then t2.bool1
WHEN t1.attr_path='bool2' then t2.bool2
WHEN t1.attr_path='bool3' then t2.bool3
WHEN t1.attr_path='bool4' then t2.bool4
END attr_value
FROM org_Extra_attr t1 inner join User_Attr_Values t2
on t1.org_id = t2.org_id
where t1.org_id=1

This is not an answer, but too long for a comment
That is a bad data model. You should not store column names in your tables.
What you could have instead is:
Org_Attr (PK = org_id + attr_no)
org_id attr_no attr_name type
1 1 desk_name STRING
1 2 citizen BOOL
2 1 perm_user BOOL
3 1 skype_id STRING
3 2 twitter STRING
Org_Attr_User (PK = org_id + attr_no + user_id)
org_id attr_no user_id value
1 1 1 b1d07
1 1 2 b2d01
1 2 1 1
1 2 2 0
2 1 3 1
2 1 4 1
3 1 5 sam_sky
3 1 6 tom_sky
3 2 5 sam_twt
3 2 6 tom_twt
With such a model data integrity would be guaranteed and querying, too, would be simple:
select oau.user_id, oa.attr_name, oau.value
from Org_Attr oa
join Org_Attr_User oau using (org_id, attr_no)
where org_id = 1;

Related

Custom aliases for all fields with GROUP BY ROLLUP

I have such tables:
Group - combination of TypeId and ZoneId
ID TypeID ZoneID
-- -- --
1 1 1
2 1 2
3 2 1
4 2 2
5 2 3
6 3 3
Object
ID GroupId
-- --
1 1
2 1
3 2
4 3
5 3
6 3
I want to build a query for grouping all these tables by TypeId and ZoneId, with number of objects which have specific combination of these field:
ResultTable
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
2 1 3
2 2 1
2 3 0
3 3 0
Query for this:
SELECT
group.TypeId,
group.ZoneId,
COUNT(obj.ID) as NumberOfObjects
FROM[Group] group
JOIN[Object] obj on obj.GroupID = group.ID
GROUP BY group.TypeId, group.ZoneId ORDER BY group.TypeId
But! I want to add summarize row after each group, and make it like:
ResultTableWithSummary
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
Summary (empty field) 3
2 1 3
2 2 1
2 3 0
Summary (empty field) 4
3 3 0
Summary (empty field) 0
The problem is that I can use GROUP BY ROLLUP(group.TypeId, group.ZoneId):
TypeId ZoneId Number of objects
-- -- --
1 1 2
1 2 1
1 null 3
2 1 3
2 2 1
2 3 0
2 null 4
3 3 0
3 null 0
but I cannot or don't know how to change not-null group.TypeId in summary rows with "Summary".
How can I do this?
The simplest method is coalesce(), but you need to be sure the types match:
SELECT COALESCE(CONVERT(VARCHAR(255), group.TypeId, 'Summary') as TypeId,
. . .
This is not the most general method, because it does not handle real NULL values in the GROUP BY keys. That doesn't seem to be an issue in this case. If it were, you could use a CASE expression with GROUPING().
EDIT:
For your particular variant (which I find strange), you can use:
SELECT (CASE WHEN group.TypeId IS NULL OR group.ZoneID IS NULL
THEN 'Summary' ELSE CONVERT(VARCHAR(255), group.TypeId)
END) as TypeId,
. . .
In practice, I would use something similar to the COALESCE() in both columns, so I don't lose the information on what the summary is for.

SAS: Need to count number of instances per id

Let's assume I have table1:
id value1 value2 value3
1 z null null
1 z null null
1 null y null
1 null null x
2 null y null
2 z null null
3 null y null
3 null null null
3 z null null
id value1 value2 value3
1 z null null
1 z null null
1 null y null
1 null null x
2 null y null
2 z null null
3 null y null
3 null null null
3 z null null
and I have table2:
id
1
2
3
I want to count number of values in each column per id to have output like this. (ex. id 1 has 2 - z's, one y and one x)
id value1 value2 value3
1 2 1 1
2 1 1 0
3 1 1 0
Need to do this in SAS. There is an example of this in Oracle but not in SAS.
If I understand correctly, this is a simple query using proc sql. For all the ids in the first table:
proc sql;
select id, count(val1) as val1, count(val2) as val2, count(val3 as val3)
from table1
group by id;
run;
count() counts the number of non-NULL values in a column or expression.

SQL Server 2012 multiple joins technique

I have appended a series of columns (11 of them) to the end my data that each act as a flag.
Here is a sample of these categories:
(Notice that multiple flags can appear for a single row!)
cat2 cat3 cat4 cat5 cat6 cat7 cat8 cat9 cat10 cat11 cat12
NULL 1 NULL NULL 2 NULL NULL 1 NULL NULL NULL
NULL NULL NULL NULL NULL NULL 2 NULL NULL NULL NULL
NULL NULL NULL 1 1 NULL NULL NULL NULL NULL NULL
NULL 1 1 NULL NULL NULL NULL NULL NULL NULL NULL
3 NULL NULL NULL NULL NULL 2 NULL NULL NULL NULL
I have a separate table that pairs each flag with a description string.
ref_id category_position description
------------------------------------------------
1 2 string description 1
2 2 string description 2
3 2 string description 3
1 3 string description 4
1 4 string description 5
1 5 string description 6
1 6 string description 7
2 6 string description 8
1 7 string description 9
1 8 string description 10
2 8 string description 11
1 9 string description 12
1 10 string description 13
1 11 string description 14
1 12 string description 15
I want to somehow join my string description against every category that is not null, so that my categories are 'human readable' and defined.
Here is a possible outcome that would be acceptable to me, based on the first row of sample data (including the header):
cat3 [join description] cat6 [join description] cat9 [join description]
1 string description 4 2 string description 8 1 string description 12
Trouble is - I don't know how to do this multi-part join. I don't want to join 12 times as this seems sloppy and perhaps won't work for the millions of rows I'm operating on.
Here's the function alternative... which isn't going to be faster than doing the joins but is useful in limited use.
create function dbo.returnDesc (#ref_id int)
returns varchar (256)
as
begin
declare #return varchar(256)
set #return = (select [description] from yourTable where ref_id = #ref_id)
return #return
end
Then in use....
select
....
dbo.returnDesc(cat2),
dbo.returnDesc(cat3),
dbo.returnDesc(cat4),
....
from
YourMainTable

Sql query null value insertion

Sql Query
SELECT respondant.respondant_firstname as first_name,
question.question_id as question_id,
answer.answer_id,
answer.answer_text,
answer.answer_rate,
answer.answer_nps,
question_radio.question_radio_text as opt
FROM question
LEFT JOIN answer on answer.answer_question_id = question.question_id
LEFT JOIN question_radio on answer.answer_question_radio_id = question_radio.question_radio_id
LEFT JOIN respondant on answer.answer_respondant_id = respondant.respondant_id
WHERE question.question_feedback_id = 1
ORDER BY question.question_id, answer.answer_id
Output:
first_name question_id answer_id answer_text answer_rate answer_nps opt
RM 1 1 5 NULL NULL
Y 1 3 0 NULL NULL
Ben 1 5 0 NULL NULL
akash 1 8 2.5 NULL NULL
RM 2 2 0 4 NULL
Y 2 4 0 3 NULL
Ben 2 6 0 0 NULL
akash 2 9 0 0 NULL
Ben 3 7 Thanks 0 0 NULL
akash 3 10 0 0 NULL
I Need the output as:
first_name question_id answer_id answer_text answer_rate answer_nps opt
RM 1 1 5 NULL NULL
Y 1 3 0 NULL NULL
Ben 1 5 0 NULL NULL
akash 1 8 2.5 NULL NULL
RM 2 2 0 4 NULL
Y 2 4 0 3 NULL
Ben 2 6 0 0 NULL
akash 2 9 0 0 NULL
RM 3 NULL 0 0 NULL
Y 3 NULL 0 0 NULL
Ben 3 NULL ThankS 0 0 NULL
akash 3 NULL 0 0 NULL
Where in third and fourth row from last doesnt have the id 3 but i need to replace as 3 and other values in that two rows has to be null
What you want to do is rather complicated. You want a row for each responder and question, and then to fill in the details for those that responded. My guess is that you want the answer_id filled in, where available. That is, your desired results should have answer_ids for "Ben" and "Akash".
The following query generates all the rows using a cross join between the responders and the questions. Then it brings in the additional information:
SELECT r.respondant_firstname as first_name, q.question_id as question_id,
a.answer_id, a.answer_text, a.answer_rate, a.answer_nps,
qr.question_radio_text as opt
FROM respondant r CROSS JOIN
question q LEFT JOIN
answer a
on a.answer_question_id = q.question_id and
a.answer_respondant_id = r.respondant_id LEFT JOIN
question_radio qr
on a.answer_question_radio_id = qr.question_radio_id
WHERE q.question_feedback_id = 1
ORDER BY q.question_id, a.answer_id;

SQL left join - and after on clause is not working

I have a scenario in left join of SQL which is not generating required output which i need. Following is description in tabular form and my tried queries,
Table A
A_ID // PK OF TABLE A
IS_ACTIVE // VALUE=1 OR 0
Table B
B_ID // PK OF TABLE B
A_ID // FK OF TABLE A IN TABLE B
Sample Records of Table A
A_ID IS_ACTIVE
1 1
2 0
3 1
4 0
5 0
Sample Records of Table B
B_ID A_ID
1 1
2 1
3 4
4 4
5 4
6 4
Select * from A left join B on A.A_ID=B.A_ID
A_ID IS_ACTIVE B_ID A_ID
1 1 1 1
1 1 2 1
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
Select * from A left join B on A.A_ID=B.A_ID and A.IS_ACTIVE=0
Following output is the actual output of above query with no effect to records by adding AND is_active=0 after ON clause.
A_ID IS_ACTIVE B_ID A_ID
1 1 1 1
1 1 2 1
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
Following output is the required output which i need to solve my problem.
A_ID IS_ACTIVE B_ID A_ID
1 1 NULL NULL
1 1 NULL NULL
2 0 NULL NULL
3 1 NULL NULL
4 0 3 4
4 0 4 4
4 0 5 4
4 0 6 4
5 0 NULL NULL
I am facing problem in getting exact records which are required.
I need all records from Table A and matching records from Table B but
those records of Table B which are equal to is_active=0 of Table A.
Note : Query should show all records of Table A
Please help me how can i get this scenario in Left Join of SQL.
I tried your examples as code. And I get the result you needed. What is the problem?
CREATE TABLE #a(a_id int, is_active bit)
CREATE TABLE #b(b_id int, a_id int)
INSERT INTO #a(a_id,is_active)
VALUES(1,1),(2,0),(3,1),(4,0),(5,0)
INSERT INTO #b(b_id,a_id)
VALUES(1,1),(2,1),(3,4),(4,4),(5,4),(6,4)
SELECT *
FROM #a as a
LEFT JOIN #b as b
ON a.a_id = b.a_id
AND a.is_active = 0
DROP TABLE #a
DROP TABLE #b
Have you tried:
Select * from A left join B on A.A_ID=B.A_ID
Where A.IS_ACTIVE=0