How to use two expression in a sub query - sql

I have two table
MARKS:
Roll
SubA (contains subject code)
SubB (contains subject code)
SubC (contains subject code)
....
....
SUBJECT:
Sub_Code
Sub_Name
Sub_Opt (contains option like theory / practical)
When I write this query
Select
Roll,
(Select Sub_Name From Subject Wwhere dbo.marks.subA = dbo.subject.sub_code),
(Select Sub_Name From Subject where dbo.marks.SubB = dbo.subject.sub_code)
from marks, subject
it runs successfully, but when I try to add subject option within subquery Like -
select
Roll,
( SELECT Sub_name ,
Sub_Opt
FROM subject
WHERE dbo.marks.SubA = dbo.subject.Sub_Code
)
( SELECT Sub_name ,
Sub_Opt
FROM subject
WHERE dbo.marks.SubB = dbo.subject.Sbu_Code
)
from Marks, subject
it causes an error:
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
I want result in this format
ROLL, SUB_NAME_A, SUB_OPT_A, SUB_NAME_B, SUB_OPT_B, SUB_NAME_C, SUB_OP_C,......

You could join on the tables, you might need a LEFT JOIN in place of inner if there's no guarantee of data in the SUBJECT table
SELECT m.Roll ,
a.Sub_name ,
a.Sub_Opt ,
b.Sub_name ,
b.Sub_Opt
FROM marks m
INNER JOIN SUBJECT a ON m.SubA = a.Sub_Code
INNER JOIN SUBJECT b ON m.SubB = b.Sub_Code
etc

select
Roll,
( SELECT Sub_name
FROM subject
WHERE dbo.marks.SubA = dbo.subject.Sub_Code
) SUB_NAME_A ,
( SELECT Sub_Opt
FROM subject
WHERE dbo.marks.SubA = dbo.subject.Sub_Code
) SUB_OPT_A ,
( SELECT Sub_name
FROM subject
WHERE dbo.marks.SubB = dbo.subject.Sub_Code
) SUB_NAME_B ,
( SELECT Sub_Opt
FROM subject
WHERE dbo.marks.SubB = dbo.subject.Sub_Code
) SUB_OPT_B ,
from Marks
this also should work

Related

Attempting to update table data from another table SQL #SQL

I have two tables, one called whoop_data with fields (id, user_id, date, recovery, sleep, strain)
and another called whoop_user with fields (id, name, average_recovery, average_sleep, average_strain)
I'd like to update the whoop_user average data fields using the whoop_data that is logged. Below are my attempts to do so.
New to SQL - please be kind :)
The code below results in the following table (works as intended):
name
AVG(recovery)
Name2
39.5
Name1
78
Name3
49.5
/*works as intended*/
SELECT name, AVG(recovery) FROM whoop_users JOIN whoop_data
ON whoop_users.id = whoop_data.user_id
GROUP BY name;
Both of the attempts below result in the following table:
Name
average_recovery
Name1
39.5
Name2
39.5
Name3
39.5
I see what is happening, but I don't understand why, how to fix it, or why the statement above DO work as intended and the ones below don't.
/*attempt 1*/
UPDATE whoop_users
SET
average_recovery = (SELECT AVG(recovery)
FROM whoop_users JOIN whoop_data ON
whoop_users.id = whoop_data.user_id
GROUP BY name)
WHERE
EXISTS (
SELECT * FROM whoop_data
WHERE whoop_data.user_id = whoop_users.id );
SELECT * FROM whoop_users;
/*attempt 2*/
WITH a AS
(
SELECT AVG(recovery) as av
FROM whoop_users JOIN whoop_data
ON whoop_users.id = whoop_data.user_id
GROUP BY name
)
UPDATE whoop_users
SET
average_recovery = (SELECT av FROM a);
SELECT * FROM whoop_users;
You must correlate properly the subquery that returns the averages from whoop_data (no joins are needed):
UPDATE whoop_users AS u
SET (average_recovery, average_sleep, average_strain) =
(
SELECT AVG(d.recovery), AVG(d.sleep) avg_sleep, AVG(d.strain)
FROM whoop_data AS d
WHERE d.user_id = u.id
);
If your version of SQLite is 3.33.0+ you could also use the UPDATE...FROM syntax which sometimes performs better:
UPDATE whoop_users AS u
SET average_recovery = d.avg_recovery,
average_sleep = d.avg_sleep,
average_strain = d.avg_strain
FROM (
SELECT user_id,
AVG(recovery) avg_recovery,
AVG(sleep) avg_sleep,
AVG(strain) avg_strain
FROM whoop_data
GROUP BY user_id
) AS d
WHERE u.id = d.user_id;

Use second condition instead if rows are not found

I have the following select:
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND lang IN ( #lv_lang, 'E' )
INTO TABLE #DATA(lt_texts).
It will select texts multiple lines of texts for a given name.
How do I say that I want texts with lang = lv_lang, but if they don't exist, then select ones with lang = 'E' all within one request to the DB and no processing on the application side?
You can use UNION operator for this task:
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND lang = #lv_lang
UNION
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND lang = 'E'
AND NOT EXISTS ( SELECT name
FROM texts
WHERE name IN #r_names
AND lang = #lv_lang
)
INTO TABLE #DATA(lt_texts).
I like coalesce for this sort of thing (it will fill-in your target with the first non-null value). You can have sy-langu as default and more languages in order of precedence:
SELECT SINGLE coalesce( default~eqktx, greek~eqktx, english~eqktx )
FROM equi AS e LEFT OUTER JOIN eqkt AS default
ON default~equnr = e~equnr
AND default~spras = #sy-langu
LEFT OUTER JOIN eqkt AS greek
ON greek~equnr = e~equnr
AND greek~spras = 'G'
LEFT OUTER JOIN eqkt AS english
ON english~equnr = e~equnr
AND english~spras = 'E'
WHERE e~equnr = #ls_equi-equnr
INTO #DATA(lv_eqktx).
Your example would become:
SELECT coalesce( default~name, english~name ),
coalesce( default~text, english~text ),
coalesce( default~lang, english~lang )
FROM texts AS default LEFT OUTER JOIN texts AS english
ON english~name = default~name
AND english~lang = 'E'
WHERE default~name IN #r_names
AND default~lang = #lv_lang
INTO TABLE #DATA(lt_texts).
Based on Suncatcher answer but without the UNION. Didn't tried, so I don't know if it works as expected:
SELECT name, text, lang FROM texts
WHERE name IN #r_names
AND ( lang = #lv_lang
OR ( NOT EXISTS ( SELECT name
FROM texts
WHERE name IN #r_names
AND lang = #lv_lang
)
AND lang = 'E'
)
)
INTO TABLE #DATA(lt_texts).

Unable to convert this legacy SQL into Standard SQL in Google BigQuery

I am not able to validate this legacy sql into standard bigquery sql as I don't know what else is required to change here(This query fails during validation if I choose standard SQL as big query dialect):
SELECT
lineitem.*,
proposal_lineitem.*,
porder.*,
company.*,
product.*,
proposal.*,
trafficker.name,
salesperson.name,
rate_card.*
FROM (
SELECT
*
FROM
dfp_data.dfp_order_lineitem
WHERE
DATE(end_datetime) >= DATE(DATE_ADD(CURRENT_TIMESTAMP(), -1, 'YEAR'))
OR end_datetime IS NULL ) lineitem
JOIN (
SELECT
*
FROM
dfp_data.dfp_order) porder
ON
lineitem.order_id = porder.id
LEFT JOIN (
SELECT
*
FROM
adpoint_data.dfp_proposal_lineitem) proposal_lineitem
ON
lineitem.id = proposal_lineitem.dfp_lineitem_id
JOIN (
SELECT
*
FROM
dfp_data.dfp_company) company
ON
porder.advertiser_id = company.id
LEFT JOIN (
SELECT
*
FROM
adpoint_data.dfp_product) product
ON
proposal_lineitem.product_id=product.id
LEFT JOIN (
SELECT
*
FROM
adpoint_data.dfp_proposal) proposal
ON
proposal_lineitem.proposal_id=proposal.id
LEFT JOIN (
SELECT
*
FROM
adpoint_data.dfp_rate_card) rate_card
ON
proposal_lineitem.ratecard_id=rate_card.id
LEFT JOIN (
SELECT
id,
name
FROM
dfp_data.dfp_user) trafficker
ON
porder.trafficker_id =trafficker.id
LEFT JOIN (
SELECT
id,
name
FROM
dfp_data.dfp_user) salesperson
ON
porder. salesperson_id =salesperson.id
Most likely the error you are getting is something like below
Duplicate column names in the result are not supported. Found duplicate(s): name
Legacy SQL adjust trafficker.name and salesperson.name in your SELECT statement into respectively trafficker_name and salesperson_name thus effectively eliminating column names duplication
Standard SQL behaves differently and treat both those columns as named name thus producing duplication case. To avoid it - you just need to provide aliases as in example below
SELECT
lineitem.*,
proposal_lineitem.*,
porder.*,
company.*,
product.*,
proposal.*,
trafficker.name AS trafficker_name,
salesperson.name AS salesperson_name,
rate_card.*
FROM ( ...
You can easily check above explained using below simplified/dummy queries
#legacySQL
SELECT
porder.*,
trafficker.name,
salesperson.name
FROM (
SELECT 1 order_id, 'abc' order_name, 1 trafficker_id, 2 salesperson_id
) porder
LEFT JOIN (SELECT 1 id, 'trafficker' name) trafficker
ON porder.trafficker_id =trafficker.id
LEFT JOIN (SELECT 2 id, 'salesperson' name ) salesperson
ON porder. salesperson_id =salesperson.id
and
#standardSQL
SELECT
porder.*,
trafficker.name AS trafficker_name,
salesperson.name AS salesperson_name
FROM (
SELECT 1 order_id, 'abc' order_name, 1 trafficker_id, 2 salesperson_id
) porder
LEFT JOIN (SELECT 1 id, 'trafficker' name) trafficker
ON porder.trafficker_id =trafficker.id
LEFT JOIN (SELECT 2 id, 'salesperson' name ) salesperson
ON porder. salesperson_id =salesperson.id
Note: if you have more duplicate names - you need to alias all of them too

Find related tags to group of tags

I am working with a system where users can attach tags to messages for easier searching/identification (just like here on SO). This is the simplified schema:
message: message_id
tag: tag_id, tag_name
tag_message: tag_id (FK), message_id (FK)
The problem I'm facing is as follows:
Given an input list of tag_id's I want to find what other tags appear in messages tagged with the inputed tags
This is the query I came up with:
SELECT
tag2.tag_name,
COUNT(*) AS tagged_message_count
FROM tag AS tag1
LEFT JOIN tag_message ON tag_message.tag_id = tag1.tag_id
LEFT JOIN message ON message.message_id = tag_message.message_id
LEFT JOIN tag_message AS tag_message2 ON tag_message2.message_id = message.message_id
LEFT JOIN tag AS tag2 ON tag_message2.tag_id = tag2.tag_id
WHERE
tag1.tag_id = ?
AND
tag1.tag_id <> tag2.tag_id
GROUP BY
tag2.tag_id;
It works BUT it works only for 1 tag and I need it to work with groups of tags.
Given tag IDs 1,2,3 we should first find all messages that are tagged with these three tags, then look at what other tags they have and return them.
I have a feeling there will have to be additional joins for each tag, but
I am not sure how to modify the query to acommodate it.
You can find messages tagged with 1, 2, and 3 using:
select tm.message_id
from tag_message tm
where tm.tag_id in (1, 2, 3)
group by tm.message_id
having count(*) = 3;
You can find other tags using:
select tag_id, count(*)
from tag_message
where message_id in (select tm.message_id
from tag_message tm
where tm.tag_id in (1, 2, 3)
group by tm.message_id
having count(*) = 3
) and
tag_id not in (1, 2, 3)
group by tag_id
order by count(*) desc;
If you want messages that have tags 1, 2, or 3, then remove the having clause.
This may look overly complex, but it at least is an alternative solution ...
-- For convience: put the arguments to the query into a CTE
-- (a temp table *could* be faster for larger sets)
-- -------------------------------------------
WITH args(tagid) AS (
VALUES (2), (3) , (4)
)
-- messages that have ALL the tags
-- [the double NOT EXISTS
-- is a standard relational-division solution]
, msg_with_all_args AS (
SELECT msgid FROM msg m
WHERE NOT EXISTS (
SELECT * FROM tag t
WHERE EXISTS (SELECT * FROM args a WHERE a.tagid = t.tagid )
AND NOT EXISTS (
SELECT * FROM msg_tag mt
WHERE mt.msgid = m.msgid
AND mt.tagid = t.tagid
)
)
)
-- additional tags, associated with
-- messages with all tags
, more_tags AS (
SELECT * FROM tag t
WHERE EXISTS (
SELECT *
FROM msg_tag mt
JOIN msg_with_all_args at ON at.msgid = mt.msgid
AND mt.tagid = t.tagid
)
-- exclude the tags that are already in the set
AND NOT EXISTS ( SELECT * FROM args nx where nx.tagid = t.tagid)
)
SELECT * FROM more_tags
;

How to join a 3rd table to previous SQL statement with 2 joined tables?

I can do simple joins effectively but I am faced with the following complex SQL statement to join to a 3rd table to display the HostName in the results as well.
I would like to add the column HostName which exists in the switches table (SwitchID is the primary key and HostName is the column).
Current statement is:
SELECT SwitchID, SUM(score)
I need to display SwitchID, HostName, SUM(score) in the results by joining the switches table.
I have tried many variations but cannot get this to work.
SELECT SwitchID
, SUM(score)
FROM ( SELECT SwitchID
, CallStackDepth * COUNT(*) AS score
FROM huntandpagingfeatures
JOIN huntgroupmembers
ON huntgroupmembers.HuntGroupDN = huntandpagingfeatures.ListDN
WHERE IsHuntGroup = 1
GROUP
BY SwitchID
, ListDN
UNION
ALL
SELECT IF( ucw.currentportid IS NOT NULL
, ucw.currentswitchid
, ucw.homeswitchid
)
AS SwitchID
, count(*) / 2 AS score
FROM userprogbuttons AS upb
INNER
JOIN usercurrentswitch AS ucw
ON upb.userdn = ucw.userdn
WHERE upb.functionid = 30
GROUP
BY SwitchID
) AS t
GROUP
BY SwitchID
SELECT t.SwitchID, SUM(score), s.HostName
FROM ( SELECT SwitchID
, CallStackDepth * COUNT(*) AS score
FROM huntandpagingfeatures
JOIN huntgroupmembers
ON huntgroupmembers.HuntGroupDN = huntandpagingfeatures.ListDN
WHERE IsHuntGroup = 1
GROUP
BY SwitchID
, ListDN
UNION
ALL
SELECT IF( ucw.currentportid IS NOT NULL
, ucw.currentswitchid
, ucw.homeswitchid
)
AS SwitchID
, count(*) / 2 AS score
FROM userprogbuttons AS upb
INNER
JOIN usercurrentswitch AS ucw
ON upb.userdn = ucw.userdn
WHERE upb.functionid = 30
GROUP
BY SwitchID
) AS t
JOIN switches s ON t.SwitchID = s.SwitchID
GROUP BY t.SwitchID, s.HostName
Subquery omitted for brevity:
select
SwitchID,
HostName,
sum(Score)
from
( ... ) as t
join
switches s on t.SwitchID = s.SwitchID
group by
SwitchID,
HostName
Just add the HostName to the group by and done.
You could group by HostName as well as switch ID:
SELECT SwitchID, SUM(score), HostName
--- skipping most of query for simplicity
) AS t
INNER JOIN Switches ON Switches.SwitchID = t.SwitchID
GROUP BY t.SwitchID, Switches.HostName
You could also use a subselect in the column list to return the host name:
SELECT SwitchID, SUM(score),
(SELECT HostName FROM Switches WHERE Switches.SwitchID = t.SwitchID) AS HostName