SQL query to find count of values in two columns - android

I have an SQLite table where I have a list of messages:
to | from
==========
9999 ME
9999 ME
ME 9999
ME 8888
The result of the query should be in the following format:
number(number of records matching)
9999 (3)
8888 (1)
How can I write a query to give me this resultset? I'm working with Android.

Maybe you can do something using this http://www.postgresql.org/docs/current/static/functions-conditional.html
I guess something like this (didn't run it):
SELECT number, count(number) as `Count` FROM (
SELECT
CASE
WHEN to = 'ME' THEN from
ELSE to
END AS number
FROM table
) GROUP BY number;

SELECT value, count(*)
FROM (
SELECT to as value
FROM your_table
UNION ALL
SELECT "from" as value
FROM your_table
) t
GROUP BY value
To filter out unwanted values for to and from use an approriate WHERE clause (ideally in the inner select to reduce the number of rows that need to be processed.

select value, sum (count) as count, from
(
select count(*) as count , "from" as value from your_table
group by "from"
union all
select count(*) as count , "to" as value from your_table
group by "to"
) t
group by t.value
Thanks #a_horse_with_no_name & #razvi!

If you are only looking to count the occurrences of numeric values this should work.
Use concatenation ("||") to form the output that you want.
SELECT to_from || ' (' || count(to_from) || ')' FROM (
SELECT
CASE
WHEN to ~ '^[0-9]+$' THEN to
WHEN from ~ '^[0-9]+$' THEN from
END to_from
FROM
testing
) a
GROUP BY to_from
Results in
?column?
----------
9999 (3)
8888 (1)
(2 rows)

Related

SQL Query, How to find the first and last value in the given table need by sequential order with conditions

I have ids in my table, ids start from 1 to 20, I want a query, to find the first and last records in a given table but I want the result by some condition.
For example: if I have the record
1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
This is the island part of the classic gaps and islands problem (With the gaps part being finding the missing values in between each island). If you search for that term, you'll find a ton of material about how to calculate them.
One approach (Requires Sqlite 3.25 or newer for window function support):
sqlite> CREATE TABLE ex(id INTEGER PRIMARY KEY);
sqlite> INSERT INTO ex VALUES (1),(2),(3),(4),(5),(9),(10),(11),(12),(13),(19),(20);
sqlite> WITH cte AS (SELECT id, id - row_number() OVER (ORDER BY id) AS grp FROM ex)
...> SELECT min(id) AS rangestart, max(id) AS rangeend FROM cte GROUP BY grp;
rangestart rangeend
---------- ----------
1 5
9 13
19 20
SQL Query to find first record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> ASC LIMIT 1
SQL Query to find last record in your table:
SELECT * FROM <table_name> ORDER BY <column_name> DESC LIMIT 1
For example: if I have the record 1,2,3,4,5,9,10,11,12,13, 19,20
I need a result like 1-5, 9-13, 19-20 like this I need results
If you need result like you have mentioned, then you can set LIMIT in your query to get how many records you can have in that query.
QUERY:
SELECT * FROM <table_name> LIMIT <any_number>

sqlite3 query by max and filter by second factor

I have:
TABLE MESSAGES
message_id | conversation_id | from_user | timestamp | message
I want:
1. SELECT * WHERE from_user <> id
2. GROUP BY conversation_id
3. SELECT in every group row with MAX(timestamp) **(if there are two same timestamps in a group use second factor as highest message_id)** !!!
4. then results SORT BY timestamp
to have result:
2|145|xxx|10000|message
6|1743|yyy|999|message
7|14|bbb|899|message
with eliminated
1|145|xxx|10000|message <- has same timestamp(10000) as message(2) belongs to the same conversation(145) but message id is lowest
5|1743|me|1200|message <- has message_from == me
example group with same timestamp
i want from this group row 3 but i get row 2 from query
SELECT max(message_timestamp), message_id, message_text, message_conversationId
FROM MESSAGES
WHERE message_from <> 'me'
GROUP BY message_conversationId
ORDER by message_Timestamp DESC
what is on my mind to do union from message_id & timestamp and then get max???
Your query is based on non-standard use of GROUP BY (I think SQLite allows that only for compatibility with MySQL) and I'm not at all sure that it will produce determinate results all the time.
Plus it uses MAX() on concatenated columns. Unless you somehow ensure that the two (concatenated) columns have fixed widths, the results will not be accurate for that reason as well.
I would write the query like this:
SELECT
m.message_timestamp,
m.message_id,
m.message_text,
m.message_conversationId
FROM
( SELECT message_conversationId -- for every conversation
FROM messages as m
WHERE message_from <> 'me'
GROUP BY message_conversationId
) AS mc
JOIN
messages AS m -- join to the messages
ON m.message_id =
( SELECT mi.message_id -- and find one message id
FROM messages AS mi
WHERE mi.message_conversationId -- for that conversation
= mc.message_conversationId
AND mi.message_from <> 'me'
ORDER BY mi.message_timestamp DESC, -- according to the
mi.message_id DESC -- specified order
LIMIT 1 -- (this is the one part)
) ;
Try below sql to achieve your purpose by group by twice.
select m.*
from
Messages m
-- 3. and then joining to get wanted output columns
inner join
(
--2. then selecting from this max timestamp - and removing duplicates
select conversation_id, max(timestamp), message_id
from
(
-- 1. first select max message_id in remainings after the removal of duplicates from mix of cv_id & timestamp
select conversation_id, timestamp, max(message_id) message_id
from Messages
where message <> 'me'
group by conversation_id, timestamp
) max_mid
group by conversation_id
) max_mid_ts on max_mid_ts.message_id = m.message_id
order by m.message_id;
http://goo.gl/MyZjyU
ok it was more simple than I thought:
basically to change select from:
max(message_timestamp)
to:
max(message_timestamp || message_id)
or max(message_timestamp + message_id)
so it will search for max on concatenation of timestamp and message_id
ps. after a digging - it's working only if message id is growing with timestamp ( order of insertion is preserved )
edit:
edit2 :
so why it works ?
SELECT max(message_timestamp+message_id), message_timestamp, message_id, message_conversationId, message_from,message_text
FROM MESSAGES
WHERE message_conversationId = 1521521
AND message_from <> 'me'
ORDER by message_Timestamp DESC

SQL SELECT from a many-to-many relationship exactly matching a set of rows

I have a SQLite3 database in an Android application with conversations and participants tables. I want a query that, given a list of participants, returns only those conversations whose participant list is an exact match of the given list of participants. For example, given these tables:
CONVERSATIONS CONVERSATION_PARTICIPANTS
------------- -------------------------
id conversation_id name
-- --------------- ----
1 1 u1
2 1 u2
3 2 u1
4 3 u1
I expect the following outputs ([participants input] => [conversations output]):
[u1] => [2, 3]
[u2] => []
[u1, u2] => [1]
I have seen solutions that involve concatenating ordered lists of participant names, but I'd rather not go that route if possible. Is such a thing possible? Without temporary tables?
To check whether two sets are not equal, based on individual items, you have to check if there exists, in either set, any item that does not exist in the other set.
Assuming we have a table Input(name), it could be implemented like this:
SELECT id
FROM Conversations
WHERE NOT EXISTS (SELECT 1
FROM Conversation_Participants
WHERE conversation_id = Conversations.id
AND NOT EXISTS (SELECT 1
FROM Input
WHERE name = Conversation_Participants.name))
AND NOT EXISTS (SELECT 1
FROM Input
WHERE NOT EXISTS (SELECT 1
FROM Conversation_Participants
WHERE conversation_id = Conversations.id
AND name = Input.name))
If you do not want to use a (temporary) table for Input, you can replace it with a subquery that returns the values on the fly:
... FROM Input ...
... FROM (SELECT 'u1' AS Name UNION ALL SELECT 'u2') AS Input ...

Filter listview populated from database doesn't display results for non latin characters [duplicate]

I want to select records from sqlite3 database by string matching. But if I use '=' in the where clause, I found that sqlite3 is case sensitive. Can anyone tell me how to use string comparing case-insensitive?
You can use COLLATE NOCASE in your SELECT query:
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
Additionaly, in SQLite, you can indicate that a column should be case insensitive when you create the table by specifying collate nocase in the column definition (the other options are binary (the default) and rtrim; see here). You can specify collate nocase when you create an index as well. For example:
create table Test
(
Text_Value text collate nocase
);
insert into Test values ('A');
insert into Test values ('b');
insert into Test values ('C');
create index Test_Text_Value_Index
on Test (Text_Value collate nocase);
Expressions involving Test.Text_Value should now be case insensitive. For example:
sqlite> select Text_Value from Test where Text_Value = 'B';
Text_Value
----------------
b
sqlite> select Text_Value from Test order by Text_Value;
Text_Value
----------------
A
b
C
sqlite> select Text_Value from Test order by Text_Value desc;
Text_Value
----------------
C
b
A
The optimiser can also potentially make use of the index for case-insensitive searching and matching on the column. You can check this using the explain SQL command, e.g.:
sqlite> explain select Text_Value from Test where Text_Value = 'b';
addr opcode p1 p2 p3
---------------- -------------- ---------- ---------- ---------------------------------
0 Goto 0 16
1 Integer 0 0
2 OpenRead 1 3 keyinfo(1,NOCASE)
3 SetNumColumns 1 2
4 String8 0 0 b
5 IsNull -1 14
6 MakeRecord 1 0 a
7 MemStore 0 0
8 MoveGe 1 14
9 MemLoad 0 0
10 IdxGE 1 14 +
11 Column 1 0
12 Callback 1 0
13 Next 1 9
14 Close 1 0
15 Halt 0 0
16 Transaction 0 0
17 VerifyCookie 0 4
18 Goto 0 1
19 Noop 0 0
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
You can do it like this:
SELECT * FROM ... WHERE name LIKE 'someone'
(It's not the solution, but in some cases is very convenient)
"The LIKE operator does a pattern
matching comparison. The operand to
the right contains the pattern, the
left hand operand contains the string
to match against the pattern. A
percent symbol ("%") in the pattern
matches any sequence of zero or more
characters in the string. An
underscore ("_") in the pattern
matches any single character in the
string. Any other character matches
itself or its lower/upper case
equivalent (i.e. case-insensitive
matching). (A bug: SQLite only
understands upper/lower case for ASCII
characters. The LIKE operator is case
sensitive for unicode characters that
are beyond the ASCII range. For
example, the expression 'a' LIKE 'A'
is TRUE but 'æ' LIKE 'Æ' is FALSE.)."
This is not specific to sqlite but you can just do
SELECT * FROM ... WHERE UPPER(name) = UPPER('someone')
Another option is to create your own custom collation. You can then set that collation on the column or add it to your select clauses. It will be used for ordering and comparisons.
This can be used to make 'VOILA' LIKE 'voilà'.
http://www.sqlite.org/capi3ref.html#sqlite3_create_collation
The collating function must return an integer that is negative, zero, or positive if the first string is less than, equal to, or greater than the second, respectively.
Another option that may or may not make sense in your case, is to actually have a separate column with pre-lowerscored values of your existing column. This can be populated using the SQLite function LOWER(), and you can then perform matching on this column instead.
Obviously, it adds redundancy and a potential for inconsistency, but if your data is static it might be a suitable option.
Its working for me Perfectly.
SELECT NAME FROM TABLE_NAME WHERE NAME = 'test Name' COLLATE NOCASE
If the column is of type char then you need to append the value you are querying with spaces, please refer to this question here . This in addition to using COLLATE NOCASE or one of the other solutions (upper(), etc).
use like this
"select * from $pwsXDataHistory where type = '$type' COLLATE NOCASE and $t_uStatus != '$DELETE' order by $t_name COLLATE NOCASE asc ");
Simply, you can use COLLATE NOCASE in your SELECT query:
SELECT * FROM ... WHERE name = 'someone' COLLATE NOCASE
you can use the like query for comparing the respective string with table vales.
select column name from table_name where column name like 'respective comparing value';

Strange behaviour of parameterized SQLite query

(Feel free to improve the question title if you can think of something better.)
Question: Consider the following SQLite query:
SELECT COUNT(*)
FROM (SELECT 1 AS value UNION SELECT 2 AS value)
WHERE value <= ?
When I use 1 as the parameter, I expect the query to yield 1, yet it yields 2. Why does this happen?
Additional information:
This is a minimal working example to reproduce the issue (Android):
Cursor c = db.rawQuery(
"SELECT COUNT(*) " +
" FROM (SELECT 1 AS value UNION SELECT 2 AS value) " +
" WHERE value <= ? ",
new String[] {String.valueOf(1)});
c.moveToFirst();
Log.i("", "Result: " + String.valueOf(c.getInt(0)));
It might have to do with the fact that the parameter is passed as a string, but, alas, there is no other way to pass parameters to the SQLite API, so I guess I'm not doing anything "wrong" here and it's SQLite's job to convert the value appropriately. I've also observed the following when using a non-parameterized version of the query (this might or might not be relevant for the issue):
SELECT COUNT(*)
FROM (SELECT 1 AS value UNION SELECT 2 AS value)
WHERE value <= 1 -- yields 1
SELECT COUNT(*)
FROM (SELECT 1 AS value UNION SELECT 2 AS value)
WHERE value <= '1' -- yields 2
It's said in the rawQuery documentation:
[...] You may include ?s in where clause in the query, which will be replaced by the values from selectionArgs. The values will be bound as Strings.
And, quoting the SQLite doc:
The results of a comparison depend on the storage classes of the
operands, according to the following rules [...]
An INTEGER or REAL value is less than any TEXT or BLOB value.
As both 1 and 2 are integers, they're both less than '1' (TEXT value). That's why this statement:
SELECT 2 <= '1'
... returns 1 in SQLite.
You should probably use ...
WHERE value <= CAST('1' AS INTEGER)
... instead. Or you can use the fact that all mathematical operators cast both operands to the NUMERIC storage class with WHERE value <= + ?, but this is less clean, imo.
Note that in this query:
SELECT * FROM myTable WHERE _id < ?
... the value of ? will get its affinity adjusted to the affinity of _id column - hence they will be compared as numbers, if _id is NUMERIC.

Categories

Resources