SQLite like query for multiple rows - iphone

I have the following table
---------------------------------------
id, type, keyword, default
---------------------------------------
1, 1, mcdonalds, 1
2 1, food, 0
3, 1, drinks, 0
4, 2, vending machine, 1
5, 2, drinks, 0
6, 3, station, 1
7, 3, travel, 0
8 3, train, 0
The idea behind this is that I want a serach query returing 'a unique' type for keywords (the default row), so when I search mcdonalds, I get the first row, but when I search food or drinks I allso get the first row.
I have done this using a subselect.
SELECT type, keyword FROM keywords WHERE type IN (SELECT DISTINCT type FROM keywords WHERE keyword like '%?%') AND `default`=1
;
This works like a charm, now however I want to be able to give multiple keywords, for example "drinks & food" I have tried
SELECT type, keyword FROM keywords WHERE type IN (SELECT DISTINCT type FROM keywords WHERE keyword like '%?%' OR keyword like'%?%') AND `default`=1
;
But then when I search for "food & drinks" in this case I would get both "the vending machine" and the "mcdonalds". However the vending machine only has an assosiated keyword "drinks" (it serves no food) So I don't want that one in my results.
When I do 'AND' instead of OR like, I get no results at all (since one row cant have both values at the same time).
Does anyone have any suggestions on how I could solve this?

Use two independent subqueries, then combine the lookups with AND:
SELECT type,
keyword
FROM keywords
WHERE type IN (SELECT DISTINCT type FROM keywords WHERE keyword like '%drinks%')
AND type IN (SELECT DISTINCT type FROM keywords WHERE keyword like '%food%')
AND "default"=1

/*
t = keywords
*/
SELECT *
FROM t
WHERE type IN (
SELECT type
FROM (
SELECT COUNT(id) AS count_matches,
type
FROM t
WHERE keyword LIKE '%drinks%'
OR keyword LIKE '%food%'
GROUP BY type
)
q
ORDER BY count_matches DESC
LIMIT 1
)
AND "default" = 1

Well, thanks for all your help. It has been figured out! :)
SELECT type, keyword FROM keywords WHERE type IN
(SELECT type FROM keywords WHERE (`keyword` LIKE '%drinks%' OR keyword like '%food%') AND `default`!= 1 GROUP BY type HAVING COUNT(id) >= 2)
AND `default`=1
The query can be built dynamically so what this does is use COUNT. The number of rows for count returned in the subquery must be at least as much as the number of keywords the user entered.
[b]edit does not seem to work, the or and like sometimes gives an higher count than expected[/b]

Related

How to reference a column in the select clause in the order clause in SQLAlchemy like you do in Postgres instead of repeating the expression twice

In Postgres if one of your columns is a big complicated expression you can just say ORDER BY 3 DESC where 3 is the order of the column where the complicated expression is. Is there anywhere to do this in SQLAlchemy?
As Gord Thompson observes in this comment, you can pass the column index as a text object to group_by or order_by:
q = sa.select(sa.func.count(), tbl.c.user_id).group_by(sa.text('2')).order_by(sa.text('2'))
serialises to
SELECT count(*) AS count_1, posts.user_id
FROM posts GROUP BY 2 ORDER BY 2
There are other techniques that don't require re-typing the expression.
You could use the selected_columns property:
q = sa.select(tbl.c.col1, tbl.c.col2, tbl.c.col3)
q = q.order_by(q.selected_columns[2]) # order by col3
You could also order by a label (but this will affect the names of result columns):
q = sa.select(tbl.c.col1, tbl.c.col2, tbl.c.col3.label('c').order_by('c')

oracle: grouping on merged columns

I have a 2 tables FIRST
id,rl_no,adm_date,fees
1,123456,14-11-10,100
2,987654,10-11-12,30
3,4343,14-11-17,20
and SECOND
id,rollno,fare,type
1,123456,20,bs
5,634452,1000,bs
3,123456,900,bs
4,123456,700,bs
My requirement is twofold,
1, i first need to get all columns from both tables with common rl_no. So i used:
SELECT a.ID,a.rl_no,a.adm_date,a.fees,b.rollno,b.fare,b.type FROM FIRST a
INNER JOIN
SECOND b ON a.rl_no = b.rollno
The output is like this:
id,rl_no,adm_date,fees,rollno,fare,type
1,123456,14-11-10,100,123456,20,bs
1,123456,10-11-12,100,123456,900,bs
1,123456,14-11-17,100,123456,700,bs
2,Next i wanted to get the sum(fare) of those rollno that were common between the 2 tables and also whose fare >= fees from FIRST table group by rollno and id.
My query is:
SELECT x.ID,x.rl_no,,x.adm_date,x.fees,x.rollno,x.type,sum(x.fare) as "fare" from (SELECT a.ID,a.rl_no,a.adm_date,a.fees,b.rollno,b.fare,b.type FROM FIRST a
INNER JOIN
SECOND b ON a.rl_no = b.rollno) x, FIRST y
WHERE x.rollno = y.rl_no AND x.fare >= y.fees AND x.type IS NOT NULL GROUP BY x.rollno,x.ID ;
But this is throwing in exceptions.
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
The expected output will be like this:
id,rollno,adm_date,fare,type
1,123456,14-11-10,1620,bs
So could someone care to show an oracle newbie what i'm doing wrong here?
It looks like there's a couple different problems here;
Firstly, you're trying to group by an x.ID column which doesn't exist; it looks like you'll want to add ID to the selected columns in your sub-query.
Secondly, when aggregating with GROUP BY, all selected columns need to be either listed in the GROUP BY statement or aggregated. If you're grouping by rollno and ID, what do you want to have happen to all the extra values for adm_date, fees, and type? Are those always going to be the same for each distinct rollno and ID pair?
If so, simply add them to the GROUP BY statement, ie,
GROUP BY adm_date, fees, type, rollno, ID
If not, you'll need to work out exactly how you want to select which one to be output; If you've got output like your example (adding in an ID column here)
ID,adm_date,fees,rollno,fare,type
1,14-11-10,100,123456,20,bs
1,10-11-12,100,123456,900,bs
1,14-11-17,100,123456,700,bs
Call that result set 'a'. If I run;
SELECT a.ID, a.rollno, SUM(a.fare) as total_fare
FROM a
GROUP BY a.ID, a.rollno
Then the result will be a single row;
ID,rollno,total_fare
1,123456,1620
So, if you also select the adm_date, fees, and type columns, oracle has no idea what you mean to do with them. You're not using them for grouping, and you're not telling oracle how you want to pick which one to use.
You could do something like
SELECT a.ID,
FIRST(a.adm_date) as first_adm_date,
FIRST(a.fees) as first_fees,
a.rollno,
SUM(a.fare) as total_fare,
FIRST(a.type) as first_type
FROM a
GROUP BY a.ID, a.rollno
Which would give the result;
ID,first_adm_date,first_fees,rollno,total_fare,first_type
1,14-11-10,100,123456,1620,bs
I'm not sure if that's what you mean to do though.

How to use postgresql any with jsonb data

Related
see this question
Question
I have a postgresql table that has a column of type jsonb. the json data looks like this
{
"personal":{
"gender":"male",
"contact":{
"home":{
"email":"ceo#home.me",
"phone_number":"5551234"
},
"work":{
"email":"ceo#work.id",
"phone_number":"5551111"
}
},
..
"nationality":"Martian",
..
},
"employment":{
"title":"Chief Executive Officer",
"benefits":[
"Insurance A",
"Company Car"
],
..
}
}
This query works perfectly well
select employees->'personal'->'contact'->'work'->>'email'
from employees
where employees->'personal'->>'nationality' in ('Martian','Terran')
I would like to fetch all employees who have benefits of type Insurance A OR Insurance B, this ugly query works:
select employees->'personal'->'contact'->'work'->>'email'
from employees
where employees->'employment'->'benefits' ? 'Insurance A'
OR employees->'employment'->'benefits' ? 'Insurance B';
I would like to use any instead like so:
select * from employees
where employees->'employment'->>'benefits' =
any('{Insurance A, Insurance B}'::text[]);
but this returns 0 results.. ideas?
What i've also tried
I tried the following syntaxes (all failed):
.. = any({'Insurance A','Insurance B'}::text[]);
.. = any('Insurance A'::text,'Insurance B'::text}::array);
.. = any({'Insurance A'::text,'Insurance B'::text}::array);
.. = any(['Insurance A'::text,'Insurance B'::text]::array);
employees->'employment'->'benefits' is a json array, so you should unnest it to use its elements in any comparison.
Use the function jsonb_array_elements_text() in lateral join:
select *
from
employees,
jsonb_array_elements_text(employees->'employment'->'benefits') benefits(benefit)
where
benefit = any('{Insurance A, Insurance B}'::text[]);
The syntax
from
employees,
jsonb_array_elements_text(employees->'employment'->'benefits')
is equivalent to
from
employees,
lateral jsonb_array_elements_text(employees->'employment'->'benefits')
The word lateral may be omitted. For the documentation:
LATERAL can also precede a function-call FROM item, but in this case
it is a noise word, because the function expression can refer to
earlier FROM items in any case.
See also: What is the difference between LATERAL and a subquery in PostgreSQL?
The syntax
from jsonb_array_elements_text(employees->'employment'->'benefits') benefits(benefit)
is a form of aliasing, per the documentation
Another form of table aliasing gives temporary names to the columns of
the table, as well as the table itself:
FROM table_reference [AS] alias ( column1 [, column2 [, ...]] )
You can use the containment operator ?| to check if the array contains any of the values you want.
select * from employees
where employees->'employment'->'benefits' ?| array['Insurance A', 'Insurance B']
If you happen to a case where you want all of the values to be in the array, then there's the ?& operator to check for that.

postgres full text search word count

I've been messing around with full text search in postgres, and I was wondering, is it possible to return total word counts of all rows?
So, let's say you have
text_col
_______
'dog'
'dog cat'
'dog bird dog'
the count of 'dog' should be four, the count of 'cat' should be one and bird should also be one.
Right now I have all the tsvectors saved to a gin indexed column as well.
Of course this would be across all rows where you could say something like
select max(ts_count(text_col_tsvector)) from mytable;
(I made that up, but I hope you get the general idea)
is it only possible to return the count of the lexeme, and if so, how does one return the dictionary (or array) of the lexeme returned.
how about:
select * from ts_stat('select text_col_tsvector from mytable')
Edit:
You mean:
with words as (
select regexp_split_to_table(text_column , E'\\W+') as word
from mytable
)
select word, count(*) as cnt from words group by 1 order by 2 desc
?

How do I count the number of objects in SQL Server 2000 and get its modified date?

I need to count the number of objects in SQL Server 2000 when restoring from the database to make sure that restore includes the latest updates. I also wanted to get the latest date an object was created or modified.
Specifically wanted to get counts for number of tables, the number of views, the number of udfs, the number of sprocs, and the date it was created or modified.
select
count(xtype) as [MyCounts],
crdate as [CreateDate],
refdate as [ModifiedDate]
from sysobjects
where xtype like 'U%'
--does not appear to be working correctly.
A very basic solution would simply use grouping and aggregating, like this:
SELECT
xtype,
total_count = COUNT(*),
last_crdate = MAX(crdate),
last_refdate = MAX(refdate)
FROM sysobjects
GROUP BY xtype
This, however, returns information on all types of objects in the current database, including those you didn't mention in your question, like constraints, keys etc.
So you might want to narrow the resulting list by applying a filter on xtype, like this:
SELECT
xtype,
total_count = COUNT(*),
last_crdate = MAX(crdate),
last_refdate = MAX(refdate)
FROM sysobjects
WHERE xtype IN ('U', 'V', 'FN', 'TF', 'IF', 'P')
GROUP BY xtype
Note that there are three types of UDFs in SQL Server. They are designated in sysobjects as follows:
FN – scalar function
TF – multi-statement table-valued function
IF – inline table-valued function
Accordingly the information about functions will be scattered in three rows if you use the above script. If you'd like to group those results in one row, your query would have to be slightly more sophisticated. For example, like this:
SELECT
type,
total_count = COUNT(*),
last_crdate = MAX(crdate),
last_refdate = MAX(refdate)
FROM (
SELECT
type = CASE
WHEN xtype = 'U' THEN 'table'
WHEN xtype = 'V' THEN 'view'
WHEN xtype = 'P' THEN 'proc'
WHEN xtype IN ('FN', 'TF', 'IF') THEN 'udf'
END,
crdate,
refdate
FROM sysobjects
WHERE xtype IN ('FN', 'TF', 'IF', 'P', 'U', 'V')
) s
GROUP BY type
Here the original types are first replaced by custom types based on the xtype value. All rows pertaining to functions are marked simply as udf, regardless of the actual function type, so in the end you can simply group by the custom type column and get the necessary totals, the information on functions now being gathered in one row.
Reference:
sys.sysobjects (Transact-SQL)
You should post the errors that you're getting so it's easier to diagnose. However, looking at your query, it's likely because you're missing a GROUP BY to accompany the COUNT aggregation you're attempting to do.
Now, the real question is how do you display a COUNT aggregation along with line-specific information like the created date?
If there are 5 views and 4 procs, what does each line look like? What are the column headers? Is the COUNT shown on each row along with the detail for that item, like this?
select
c.cnt as [MyCounts],
s.name as [Name],
s.xtype as [Type],
s.crdate as [CreateDate],
s.refdate as [ModifiedDate]
from
sysobjects s
inner join (select COUNT(1) cnt, xtype from sysobjects group by xtype) c
on s.xtype = c.xtype
where
s.xtype like 'U%'