Select query and unknow predicate value - sql

I have this table with this columns: Key,Type,Culture.
Key and Culture are PK.
I know the Key and Culture values before query execution.
select * from resources r where r.CULTURE = 'sk' and r.KEY = 'test';
But I'd like to perform a select which would select also all other records with the same Type as the record with Culture = 'sk' and Key = 'test'

SELECT * FROM resources AS r WHERE r.type =
(SELECT type FROM resources s where s.culture= 'sk' and s.key= 'test')
culture and key are PK, so you only get one value back for type for your subquery. That result is used to get all rows with that type. If the subquery would return multiple values, you can use IN instead of =.

Use a join, it could resolves performance issue.
SELECT r.*
FROM resources r
INNER JOIN resources r2
ON r.type = r2.type
WHERE r2.CULTURE = 'sk'
AND r2.KEY = 'test'

Related

Exists, or Within

I'm writing a some filtering logic that basically wants to first check if there's a value in the filter table, then if there is return the filtered values. When there isn't a value in the filter table just return everything. The following table does this correctly but I have to write the same select statement twice.
select *
from personTbl
where (not exists (select filterValue from filterTable where filterType = 'name') or
personTbl.name in (select filterValue from filterTable where filterType = 'name'))
Is there some better way to do this that will return true if the table is empty, or the value is contained within it?
One approach is to do a left outer join to your filter-subquery, and then select all the rows where the join either failed (meaning that the subquery returned no rows) or succeeded and had the right value:
SELECT personTbl.*
FROM personTbl
LEFT
OUTER
JOIN ( SELECT DISTINCT filterValue
FROM filterTable
WHERE filterType = 'name'
) filter
ON 1 = 1
WHERE filter.filterValue = personTbl.name
OR filter.filterValue IS NULL
;
To be honest, I'm not sure if the above is a good idea — it's not very intuitive1, and I'm not sure how well it will perform — but you can judge for yourself.
1. As evidence of its unintuitiveness, witness the mistaken claim below that it doesn't work. As of this writing, that comment has garnered two upvotes. So although the query is correct, it clearly inspires people to great confidence that it's wrong. Which is a nice party trick, but not generally desirable in production code.
You can use a collection to try to make the query more intuitive (and only require a single select from the filter table):
CREATE TYPE filterlist IS TABLE OF VARCHAR2(100);
/
SELECT p.*
FROM PersonTbl p
INNER JOIN
( SELECT CAST(
MULTISET(
SELECT filterValue
FROM filterTable
WHERE filterType = 'name'
)
AS filterlist
) AS filters
FROM DUAL ) f
ON ( f.filters IS EMPTY OR p.name MEMBER OF f.filters );

Case statement causing error - No column name was specified for column 4 of 'a'

This query is designed so that each row will have a unique NAME value.
The query works fine except when I introduced the SELECT CASE statement to select ROLENAME. When this is added, I get the error
No column name was specified for column 4 of 'a'.
How can this be resolved?
SELECT *
FROM
(SELECT
NAME, CREATE_DATE, OTHERNAME,
CASE
WHEN ID = 'test' THEN ROLENAME
END,
STATUS,
ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY NAME DESC) AS RowNumber
FROM
BUSRULES br1
INNER JOIN
BUSRULES br2 ON br1.ID_NUM = br2.ID_NUM
INNER JOIN
BUSRULES br3 ON br1.ID_NUM = br3.ID_NUM
INNER JOIN
dbo.MDI ON NAME = br3.VALUE_TXT
INNER JOIN
(PERSON
LEFT JOIN
OP_TYPE ON OP_CASE.ID_NUM = OP_TYPE.ID_NUM)
ON OP_ID_NUM = OCO_OP_ID_NUM
WHERE
br1.KEY_TXT = 'EVENT'
AND br1.VALUE_TXT = 'YES'
AND br2.ABR_KEY_TXT = 'LIST'
AND br2.ABR_VALUE_TXT = 'test'
AND br3.ABR_KEY_TXT = 'NAME') AS a
WHERE
a.RowNumber = 1
Try to modify :
CASE WHEN ID = 'test'
THEN ROLENAME
END
into :
CASE WHEN ID = 'test'
THEN ROLENAME
END AS ROLENAME
The reason is because when you use CASE clause, it will have no name, unless you define it
The error message is very specific - you don't have a column alias for the column you're creating with your CASE statement.
, CASE WHEN ID = 'test'
THEN ROLENAME
END AS SomeColumnName -- Add a column name alias here
It's extremely important to learn to actually read the words in error messages. They usually contain some relevant information. It's also important to learn where to find the documentation for the DBMS you're using, as it would have told you what the requirements were for CASE and shown examples of using it.

sql query for multi valued attributes

I have resources each represented by a guid and they have attribute name-value pairs. I would like to query
for resources which have the given attribute name value pairs.
So, suppose the table looks like:
GUID ATTR_SUBTYPE ATTR_VAL
63707829116544a38c5a508fcde031a4 location US
63707829116544a38c5a508fcde031a4 owner himanshu
44d5bf579d9f4b9a8c41429d08fc51de password welcome1
44d5bf579d9f4b9a8c41429d08fc51de host retailHost
c67d8f5d1a9b41428f029d55b79263e1 key random
c67d8f5d1a9b41428f029d55b79263e1 role admin
and I want all the resources with location as US and owner as olaf.
One possible query would be:
select guid from table where attr_subtype = 'location' and attr_value = ‘US'
INTERSECT
select guid from table where attr_subtype = 'owner' and attr_value = ‘himanshu';
There can be any number of attribute name value pairs in the query, so an additional intersection per pair
in the query. I was wondering if we can construct a better query as intersection is expensive.
Assuming you don't have duplicate attributes per GUID you can achieve the desired result without a JOIN:
SELECT "GUID" FROM T
WHERE ( "ATTR_SUBTYPE" = 'location' AND "ATTR_VAL" = 'US' )
OR ( "ATTR_SUBTYPE" = 'owner' AND "ATTR_VAL" = 'himanshu' )
GROUP BY "GUID"
HAVING COUNT(*) = 2 -- <-- keep only GUID have *both* attributes
See http://sqlfiddle.com/#!4/80900/2
Generally, JOIN would be better than INTERSECT here. It gives a chance to get first records prior than several full table scans will finish. But anyway you select a slow data structure so it wouldn't wonderful if it slowdown.
Try something like
select *
from
(select * from table where attr_subtype = 'location' and attr_value = 'US') t1
join
(select * from table where attr_subtype = 'owner' and attr_value = 'himanshu') t2
on (t1.guid = t2.guid)
...
Insert your targets into a temp table then join to it.
select t.guid
from table as t
join temp
on t.attr_subtype = temp.attr_subtype
and t.attr_value = temp.attr_value

SQL - GROUPING, ID, NAME

I was wondering what the answer should be for 1c. on this website:
http://sqlzoo.net/6.htm
SELECT company
FROM route WHERE stop=4
GROUP BY name, ID
this obviously isn't working, the ID and name isn't showing up no matter what. What is missing here? Thanks.
SELECT stops.id, stops.name
FROM route
INNER JOIN stops on route.stop = stops.id
WHERE route.num = 4 AND route.company = 'LRT'
You need to join the tables as the data you want to return is in a different table to the one which filters the data.
This works and does not include any unnecessary table joins. A good rule of thumb is to use EXISTS to verify values in a table that you do not need the output for. Otherwise, you would use a JOIN
SELECT stops.id, stops.name
FROM stops
WHERE EXISTS
(
SELECT 1 FROM route
WHERE route.stop = stops.id AND num = '4' AND company = 'LRT'
)
select s.id, s.name
from stops s
inner join route r
on s.id = r.stop
where r.num= 4
AND r.company= 'LRT'
It gives you this error:
sql: Unknown column 'name' in 'group statement'
There is no name in route tabel.
The tables structure are:
stops(id, name)
route(num,company,pos, stop)
So the answer for this quiz is:
SELECT s.id, s.name
FROM route r, stops s
WHERE r.stop= s.id
and r.num = 4 AND r.company = 'LRT'

selecting latest rows per distinct foreign key value

excuse the title, i couldn't come up with something short and to the point...
I've got a table 'updates' with the three columns, text, typeid, created - text is a text field, typeid is a foreign key from a 'type' table and created is a timestamp. A user is entering an update and select the 'type' it corresponds too.
There's a corresponding 'type' table with columns 'id' and 'name'.
I'm trying to end up with a result set with as many rows as is in the 'type' table and the latest value from updates.text for the particular row in types. So if i've got 3 types, 3 rows would be returned, one row for each type and the most recent updates.text value for the type in question.
Any ideas?
thanks,
John.
select u.text, u.typeid, u.created, t.name
from (
select typeid, max(created) as MaxCreated
from updates
group by typeid
) mu
inner join updates u on mu.typeid = u.typeid and mu.MaxCreated = u.Created
left outer join type t on u.typeid = t.typeid
What are the actual columns you want returned?
SELECT t.*,
y.*
FROM TYPE t
JOIN (SELECT u.typeid,
MAX(u.created) 'max_created'
FROM UPDATES u
GROUP BY u.typeid) x ON x.typeid = t.id
JOIN UPDATES y ON y.typeid = x.typeid
AND y.created = x.max_created
SELECT
TYP.id,
TYP.name,
TXT.comment
FROM
dbo.Types TYP
INNER JOIN dbo.Type_Comments TXT ON
TXT.type_id = TYP.id
WHERE
NOT EXISTS
(
SELECT
*
FROM
dbo.Type_Comments TXT2
WHERE
TXT2.type_id = TYP.id AND
TXT2.created > TXT.created
)
Or:
SELECT
TYP.id,
TYP.name,
TXT.comment
FROM
dbo.Types TYP
INNER JOIN dbo.Type_Comments TXT ON
TXT.type_id = TYP.id
LEFT OUTER JOIN dbo.Type_Comments TXT2 ON
TXT2.type_id = TYP.id AND
TXT2.created > TXT.created
WHERE
TXT2.type_id IS NULL
In either case, if the created date can be identical between two rows with the same type_id then you would need to account for that.
I've also assumed at least one comment per type exists. If that's not the case then you would need to make a minor adjustment for that as well.