Query on both component parts and aggregation - sql

I'm using sqlite3. I'd prefer to continue using it but switching is not out of the question.
I have two tables:
card face
---- ----
id id
card_id
name
position
A card has one or more entries in face.
The canonical name of a card is the name of its faces concatenated with ' // ' in position order.
Example cards: 'Dark Ritual', a card with one face named 'Dark Ritual'. 'Research // Development', a card with two faces named 'Research' and 'Development'
How can I write a SELECT that finds the details of a card regardless of whether the search term is the canonical name or the name of one of the card's faces?
This is how far I got:
SELECT id, GROUP_CONCAT(name, ' // ')
FROM
(SELECT c.id, f.name
FROM card AS c
INNER JOIN face AS f ON c.id = f.card_id
ORDER BY f.card_id, f.position)
GROUP BY id
HAVING GROUP_CONCAT(name, ' // ') IN (?)
OR name IN (?)
This finds the card I am looking for when ? = the canonical name ('Research // Development'.) It also finds what I am looking for when I supply the name of a card with only one face ('Dark Ritual') or the second of two face names ('Development'). It does not work when I supply the first of two face names ('Research'). I know the outer query can 'see' both names because they are both present in the GROUP_CONCAT form of the name. But querying on the first face name doesn't work in the HAVING.
Can you help me understand why? Thanks!

Try this version:
HAVING SUM(CASE WHEN name IN (?) THEN 1 ELSE 0 END) > 0
Do note that parameters cannot match lists. If you want to pass in a comma delimited list, you can try:
HAVING SUM(CASE WHEN ',' || name || ',' LIKE '%,' || ? || ',%' THEN 1 ELSE 0 END) > 0

Related

how to select a row if multiple values are in a related table

I am trying to make a filter to find all the stuffs made of various substances.
In the database, there is:
a stuffs table
a substances table
a stuffs_substances join table.
Now, I want to find only all the stuffs that are made of gold AND silver (not all the stuffs that contain gold and all stuffs that contain silver).
One last thing: the end user can type only a part of the substance name in the filter form field. For example he will type silv and it will show up all the stuffs made of silver.
So I made this query (not working):
select "stuffs".*
from "stuffs"
inner join "stuffs_substances" as "substances_join"
on "substances_join"."stuff_id" = "stuffs"."id"
inner join "substances"
on "substances_join"."substance_id" = "substances"."id"
where ("substances"."name" like '%silv%')
and ("substances"."name" like '%gold%')
It returns an empty array. What am I doing wrong here?
Basically, you just want aggregation:
select st.*
from "stuffs" st join
"stuffs_substances" ss join
on ss."stuff_id" = st."id" join
"substances" s
on ss."substance_id" = s."id"
where s."name" like '%silv%' or
s."name" like '%gold%'
group by st.id
having count(*) filter (where s."name" like '%silv%') > 0 and
count(*) filter (where s."name" like '%gold%') > 0;
Note that this works, assuming that stuff.id is the primary key in stuffs.
I don't understand your fascination with double quotes and long table aliases. To me, those things just make the query harder to write and to read.
if you want to do search by part of word then do action to re-run query each time user write a letter of word , and the filter part in query in case of oracle sql way
in case to search with start part only
where name like :what_user_write || '%'
or in case any part of word
where name like '%' || :what_user_write || '%'
you can also use CAB, to be sure user can search by capital or small whatever
ok, you ask about join, I test this in mysql , it work find to get stuff made from gold and silver or silver and so on, hope this help
select sf.id, ss.code, sf.stuff_name from stuffs sf, stuffs_substances ss , substances s
where sf.id = ss.id
and s.code = ss.code
and s.sub_name like 'gol%'

Ordery By and Like is not working as expected

I am working on the autosearch UI where, I need to search the names listed from the letter I search in text box. Ex. You can take as search box works in Google search engine, where when we start entering letters it shows list under that searched text.
Below is the query where I am getting results when using LIKE but it is not working as expected, there are two screen shots, currently I am getting the results like first pic.
I even feel even order by is also not working.
NOTE :
I need both Order By.
Even though there are list which starts from d it is not displaying at first.
Second Pic :
If you see the second results are coming correct, but why should I remove the wild card syntax under ProfileName.
Let me know how I should fix this.
Your result is correctly showing what you've written in your query.
The first result shows all records with
IsPrimary = 1 AND LastName starts with 'd', OR
ProfileName starts with 'd'
The second result shows records with
IsPrimary = 1 AND LastName starts with 'd', OR
ProfileName = 'd'
Note that LIKE is case insensitive.
If you want to prioritize the search to LastName then to ProfileName.
Do this
Select * FROM
(
SELECT [your list here], 1 as priority
FROM Employee E
INNER JOIN [your list here]
WHERE
EP.IsPrimary = 1 AND E.LastName LIKE #SearchByChar + '%'
UNION
SELECT [your list here], 2 as priority
FROM Employee E
INNER JOIN [your list here]
WHERE
P.ProfileName LIKE #SearchByChar + '%'
) s
ORDER BY priority
If you want name matches to appear above profile matches, you could just do something like:
ORDER BY
CASE WHEN E.LastName LIKE #SearchByChar + '%' THEN 0 ELSE 1 END,
E.LastName,
P.ProfileName

Oracle Query on Comma Separated Values In a Column

I have two tables. One table is a list of access points. The other is a list of who has access to what.
It use to be a person had access to either one thing or all things. But, now that has changed. Now someone might have access to several points.
The wrench in the system is that in the column that shows what they have access to may have a single value, "ALL" for all access or a comma separated list (which is new).
I originally thought I could just do WHERE Access_To Here IN(), but I am unsure how to convert the value of Access_To to a formatted list.
I need to be able to do this as a single query since I am using it for a LOV in APEX.
So, I need some help.
The Access_Points_Table has only one column, Access_Points. Here are some example values that it might have:
CSX
CZR
XR3
NBO
QHG
The Users_List table has several columns, but the most important are User_Name, Access_To. Here are some example values that it might have:
Joe | ALL
Fred | CSX
Allen | CZR, NBO
Hank | QHG
Here is query I am currently using, but it only works if there is only a single value in the Access_To column.
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN
Users_List ul
ON (ul.wwid = 'ZZ999'
AND (ul.Access_To = 'ALL' OR apt.symbol_name = ul.Access_To))
ORDER BY name ASC
What I am trying to accomplish is:
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN
Users_List ul
ON (ul.wwid = 'ZZ999'
AND (ul.Access_To = 'ALL' OR apt.symbol_name IN(Something Goes Here)))
ORDER BY name ASC
So, if run a query for the user Allen, it will return the rows:
CZR
NBO
Depending on the length of your comma-delimited data, you can use Oracle's regular expression engine (the difficulty is that Oracle limits regular expressions to 512 bytes):
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN Users_List ul
ON ( ul.wwid = 'ZZ999'
AND ( ul.Access_To = 'ALL'
OR REGEXP_LIKE(apt.symbol_name, '^(' || REPLACE(ul.Access_To, ',', '|') || ')$') ) )
ORDER BY name ASC
Alternately you can use LIKE:
SELECT DISTINCT Access_Points VALUE
FROM Access_Points_Table apt
JOIN Users_List ul
ON ( ul.wwid = 'ZZ999'
AND ( ul.Access_To = 'ALL'
OR ',' || ul.Access_To || ',' LIKE '%,' || apt.symbol_name || ',%' ) )
ORDER BY name ASC
Note that if Access_To has spaces after its commas as in your OP, it does add some complexity but that can be overcome, simply REPLACE(ul.Access_To, ' ').
By the way, I do wonder why this: ul.wwid = 'ZZ999' is in the ON clause instead of in a WHERE clause.
Another way would be to use the instr test:
SELECT DISTINCT access_points VALUE
FROM access_points_table a
JOIN users_list u
ON ( INSTR(u.access_to,a.access_points,1) > 0 )
ORDER BY 1

Pivot without number gets null

I've tried writing a simple pivot function with SQL. My main problem is that all the tables involved have no numbers in them. I think that this is the main reason I get only one result and the rest is NULL even though some of the cells have data in them.
This is my function:
SELECT Inspector, ShiftManager, ForkliftOperator, CalibrationOperator, sorted
FROM (SELECT LHAPP_OWNER.dbo.Personnel.FirstName + ' ' + LHAPP_OWNER.dbo.Personnel.LastName
AS name, LHAPP_OWNER.dbo.PersonnelGroup.PersonnelGroup
FROM MachineGroup
INNER JOIN _MG_CurrentEmployee ON MachineGroup.MachineGroupID = _MG_CurrentEmployee.MachineGroupID
INNER JOIN LHAPP_OWNER.dbo.PersonnelGroup ON _MG_CurrentEmployee.PerssonelGroupID = LHAPP_OWNER.dbo.PersonnelGroup.PersonnelGroupID
LEFT OUTER JOIN LHAPP_OWNER.dbo.Personnel ON _MG_CurrentEmployee.PerssonelID = LHAPP_OWNER.dbo.Personnel.PersonnelID
WHERE (MachineGroup.MachineGroup = N'Line 1')) tbl PIVOT
( MIN(name) FOR PersonnelGroup IN (Inspector, ShiftManager, ForkliftOperator, CalibrationOperator, sorted) ) tbl2
I didn't know what to do with the name so I just gave it MIN. I know it's probably wrong.
Could someone give me a hand here?
My main Table of the inner select looks like this:
name || PersonnelGroup
---------------------------------------
ron roni || Inspector
Lighthouse Systems || Shift Manager
gali logi || Forklift Operator
NULL || Calibration Operator
NULL || sorted
After the function I get this:
Inspector|ShiftManager|ForkliftOperator|CalibrationOperator|Sorted
------------------------------------------------------------------------
ron roni | NULL | NULL | NULL | NULL
It's OK now. I found out the problem. Some of the PersonnelGroup names had two words in them and I looked for one word combining the tow words and so it didn't find any answers. For example, I changed 'ShiftManager' into '[Shift Manager]'

How to use SELECT DISTINCT and CONCAT in the same SQL statement

So I am feeding the results of this SQL into an array. The array later becomes the suggestions for a textbox that operates while typing. I want it to only return each name 1 time, even if the person has multiple appointments. Currently, this returns all appointments for the person with that name, so if "Brad Robins" has 5 appointments, and I start to type "Brad", it displays "Brad Robins" 5 times in the suggestions instead of only once.
$sql = "SELECT DISTINCT CONCAT(clients.studentFirstName, ' ', clients.studentLastName) AS name, appointments.location, appointments.subLocation, appointments.appointmentAddress1, appointments.appointmentAddress2, appointments.appointmentCity, appointments.appointmentState, appointments.appointmentZip, appointments.startTime, appointments.endTime, appointments.date, clients.school
FROM appointments JOIN clients
ON appointments.clientID = clients.clientID
WHERE CONCAT(clients.studentFirstName, ' ', clients.studentLastName) = '".$roommate."' AND clients.school = '".$school."';";
To me, it just seems like DISTINCT and CONCAT aren't playing nicely together.
The problem are the other fields; DISTINCT applies to the whole result. Probably the best thing is to do to separate queries or populate 2 different arrays; if you ORDER BY name, you can remove duplicates by copying into the dest array only when the name changes.
Don't use DISTINCT, use group by:
$sql = "SELECT CONCAT(clients.studentFirstName, ' ', clients.studentLastName) AS name, appointments.location, appointments.subLocation, appointments.appointmentAddress1, appointments.appointmentAddress2, appointments.appointmentCity, appointments.appointmentState, appointments.appointmentZip, appointments.startTime, appointments.endTime, appointments.date, clients.school
FROM appointments JOIN clients
ON appointments.clientID = clients.clientID
WHERE CONCAT(clients.studentFirstName, ' ', clients.studentLastName) = '".$roommate."' AND clients.school = '".$school."' group by CONCAT(clients.studentFirstName, ' ', clients.studentLastName);";
Also be careful about XSS in $school and $roomate if this accessible outside.
Distinct goes against the entire row of ALL columns, not just the name portion... So if the appointments are on different date/times, locations, etc, they will all come out. If all you want to show is the NAME portion, strip the rest of the other content. Query the available appointments AFTER a person has been chosen.
You could use
group by name
at the end, which would cause the query to return only one of each name, but then you can't predict what appointment results will be returned in cases where a client has multiple appointments, and the query stops being very useful.
Like others have pointed out, you should probably just get the list of appointments after the client has been chosen.
select colA||' - '||colB
from table1
where colA like 'beer%'
group by colA||' - '||colB
order by colA||' - '||colB
;
SELECT DISTINCT MD5(CONCAT(clients.studentFirstName, ' ', clients.studentLastName)) as id, appointments.location, appointments.subLocation, ...
Select distinct concat(....)
From ....