select query - each two row into single result row - android

Lets assume I have the following 'Products' table:
ProductID | ProductName
----------+---------
0255463 | ProductA
0254483 | ProductB
0255341 | ProductC
0905454 | ProductD
Is there a way (in Android's SQLite) to select each two consecutive rows into single result row?
Here is the desired query result:
FirstProductID | FirstProductName | SecondProductID | SecondProductName
---------------+------------------+-----------------+---------
0255463 | ProductA | 0254483 | ProductB
0255341 | ProductC | 0905454 | ProductD
I would like a generic solution that can be used to any table, regardless the table content.

Create a temporary table with an auto increment column
CREATE TEMP TABLE temp(
id int not null primary auto increment,
pid int,
pname text,
);
Insert select this data into the temporary table
INSERT INTO temp (pid, pname) SELECT * FROM Products;
Join the temporary table on id = id + 1 where the first instance has id % 2 = 0
SELECT t1.pid AS t1_pid, t1.pname AS t1_pname,
t2.pid AS t2_pid, t2.pname AS t2_pname
FROM temp as t1 LEFT JOIN temp AS t2
ON t1.id + 1 = t2.id
WHERE t1.id % 2 = 0;

Single query (not faster):
SELECT
First.ProductId AS FirstProductId,
First.ProductName AS FirstProductName,
Second.ProductId AS SecondProductId,
Second.ProductName AS SecondProductName
FROM
(SELECT *, Cnt/2 AS Line FROM (
SELECT *, (
SELECT COUNT() FROM Products AS _ WHERE ROWID<Products.ROWID
) AS Cnt FROM Products WHERE Cnt%2=0
)) AS First
LEFT JOIN
(SELECT *, Cnt/2 AS Line FROM (
SELECT *, (
SELECT COUNT() FROM Products AS _ WHERE ROWID<Products.ROWID
) AS Cnt FROM Products WHERE Cnt%2=1
)) AS Second
ON First.Line = Second.Line
ORDER BY First.Line;
If you need a faster solution, #kzarns proposed a good one.

It is easy if you have consecutive IDs for the rows:
SELECT t1.productID AS productID1, t1.ProductName AS productName1, t2.productID AS productID2, t2.ProductName AS ProductName2
FROM product t1
JOIN product t2 WHERE t1.id+1 = t2.id
WHERE MOD(t1.id,2) = 1
ORDER BY t1.id

this is the sample idea , basically what am i doing is obtain different result from 2 select statement and join it , have a try on it, cheer =)
--sample table
DECLARE #SAMPLE TABLE
(
ProductID INT,
ProductName NVARCHAR(255)
)
--sample data
INSERT INTO #SAMPLE
VALUES
('1','ProductA'),
('2','ProductB'),
('3','ProductC'),
('4','ProductD')
SELECT FirstProductID,FirstProductName,SecondProductID,SecondProductName FROM
(
--Query to get firstProduct items
SELECT ROW_NUMBER() OVER (ORDER BY FirstProductID) firstrn,FirstProductID,FirstProductName
FROM(
SELECT ProductID 'FirstProductID',ProductName 'FirstProductName' FROM
(
SELECT ROW_NUMBER() OVER (Order by productid) firstrn1,ProductID,ProductName
FROM #SAMPLE
) FIRSTPRODUCTTABLE
WHERE firstrn1%2 = 1
) FIRSTPRODUCTTABLE1
)t1
-A join is performed
LEFT OUTER JOIN (
--Query to get second product items
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY SecondProductID) rownumber,SecondProductID,SecondProductName
FROM(
SELECT ProductID 'SecondProductID',ProductName 'SecondProductName' FROM
(
SELECT ROW_NUMBER() OVER (Order by productid) rn,ProductID,ProductName
FROM #SAMPLE
) SECONDPRODUCTTABLE
WHERE rn%2 = 0
)SECONDPRODUCTTABLE1
) t2
) t3 ON t1.firstrn=t3.rownumber

Doing this in SQL would be rather complex and slow.
The cleanest way to reorder column values like this would be to implement your own Cursor class that wraps the original database cursor, but doubles the number of columns, and redirects all column accesses to the appropriate record.
Something like this:
class DoubleColumnCursor implements Cursor {
Cursor baseCursor;
int baseColumns;
int currentPosition;
public getColumnCount() {
return baseColumns * 2;
}
public String getColumnName(int columnIndex) {
if (columnIndex < baseColumns)
return baseCursor.getColumnName(columnIndex) + "1";
else
return baseCursor.getColumnName(columnIndex - baseColumns) + "2";
}
public boolean moveToPosition(int position) {
boolean result = baseCursor.moveToPosition(position * 2);
if (result)
currentPosition = position;
return result;
}
public String getString(int columnIndex) {
if (columnIndex < baseColumns) {
baseCursor.moveToPosition(currentPosition * 2);
return baseCursor.getString(columnIndex);
} else {
baseCursor.moveToPosition(currentPosition * 2 + 1);
return baseCursor.getString(columnIndex - baseColumns);
}
}
...
};

Related

SQLite - Fastest way to count inside selects

I have an application that makes quite a lot of selects, listing its results and allowing the user edit certain values.
The core of my question is if its possible to improve my queries or not given the following query in SQLite:
"SELECT X.data1, X.data2, count(X.id_X) as Quantity_Itens,
(select count(*) from Table2 where id_X=X.id_X) local_Table2,
(select count(*) from Table3 inner join Table2 on Table3.id_Table2 = Table2.id where Table3.id_X=X.id_X and type=1) Quantity_Type1,
(select count(*) from Table3 inner join Table2 on Table3.id_Table2 = Table2.id where Table3.id_X=X.id_X and type=2) Quantity_Type2,
(select count(*) from Table4 where id_X = X.id_X) Quantity_Other,
(select count(*) from Table2 where id_X = X.id_X and status <10) Total_Data FROM Table1 X where (X.type_item = 2 or X.type_item = 4 or X.type_item = 6 or X.type_item = 8) and X.ative = 1 and id_local != 0 group by X.id_X order by X.Alias1"
I am not sure if using promisses will improve in any way this, as I need all those datas before allowing the user to take control again.
Also, may or may not be relevant:
OS: Android 4+
Frameworks: Ionic1, AngularJS, Cordova
In your query there are 5 subqueries executed for each of the rows of Table1.
Only one of them accesses only once 1 table.
2 of them access the same table twice and 2 of them access 2 joined tables twice.
This means there are multiple scans through the same tables for each of the rows of Table1.
Also you are aggregating inside Table1, with GROUP BY id_X, but you have in the SELECT list 2 columns: data1 and data2, which are not included in the GROUP BY clause. This means that the returned values of these columns are arbitrary.
And what is that column Alias1? There is no such column among the returned columns of the query.
Anyway, I suggest that you aggregate first in each table, or join of tables and then join to Table1.
Like this:
SELECT t1.data1,
t1.data2,
t1.Quantity_Items,
t2.local_Table2,
t3.Quantity_Type1,
t3.Quantity_Type2,
t4.Quantity_Other,
t2.Total_Data
FROM (
-- The values returned for data1, data2 and Alias1 will be arbitrary
SELECT id_X, data1, data2, Alias1, COUNT(*) Quantity_Items
FROM table1
GROUP BY id_X
) t1
INNER JOIN (
SELECT id_X, COUNT(*) local_Table2, SUM(status < 10) Total_Data
FROM Table2
GROUP BY id_X
) t2 ON t2.id_X = t1.id_X
INNER JOIN (
SELECT t3.id_X, SUM(type = 1) Quantity_Type1, SUM(type = 2) Quantity_Type2
FROM Table3 t3 INNER JOIN Table2 t2
ON t3.id_Table2 = t2.id
GROUP BY t3.id_X
) t3 ON t3.id_X = t1.id_X
INNER JOIN (
SELECT id_X, COUNT(*) Quantity_Other
FROM Table4
GROUP BY id_X
) t4 ON t4.id_X = t1.id_X
WHERE t1.type_item IN (2, 4, 6, 8)
AND t1.active = 1 AND t1.id_local <> 0
ORDER BY t1.Alias1

Why we can't use select * for particular table in join queries?

I want records from multiple tables. My requirement is to get all columns form one table and some interested columns from different table(s).
We can use "select * from table_name" for normal fetch query. But this is not working with join query, I have to write all column name for that table.
So, Is there any way to use * to fetch records in join query?
SELECT Item.ItemId, Item.Item, ItemIndex.CurrentAverageScore, ItemStyle.ItemStyleId, ItemStyle.ItemStyle, Brewery.BreweryId, Brewery.BreweryDescription, (SELECT PersonalRating FROM UserItemJournal WHERE ItemId = ItemIndex.ItemId) As PersonalRating, (SELECT Notes FROM UserItemJournal WHERE ItemId = ItemIndex.ItemId AND PersonalRating IS NOT NULL) As Notes FROM Item, ItemIndex, ItemStyle, Brewery, UserItemJournal WHERE Item.ItemId = ItemIndex.ItemId AND Item.LanguageId = 1 AND ItemStyle.ItemStyleId = ItemIndex.ItemStyleId AND ItemStyle.LanguageId = 1 AND Brewery.BreweryId = ItemIndex.BreweryId AND Brewery.LanguageId = 1 AND UserItemJournal.PersonalRating IS NOT NULL
In above query I want all columns from Item Table.
I think * has limitation, We can use * with single table only.Please correct me if I'm wrong.
Thanks
You can use that table name or aliase name and then dot star (item.*),
and other table columns as you like with their aliase or table name.
SELECT Item.*, ItemIndex.CurrentAverageScore, ItemStyle.ItemStyleId, ItemStyle.ItemStyle, Brewery.BreweryId, Brewery.BreweryDescription, (SELECT PersonalRating FROM UserItemJournal WHERE ItemId = ItemIndex.ItemId) As PersonalRating, (SELECT Notes FROM UserItemJournal WHERE ItemId = ItemIndex.ItemId AND PersonalRating IS NOT NULL) As Notes FROM Item, ItemIndex, ItemStyle, Brewery, UserItemJournal WHERE Item.ItemId = ItemIndex.ItemId AND Item.LanguageId = 1 AND ItemStyle.ItemStyleId = ItemIndex.ItemStyleId AND ItemStyle.LanguageId = 1 AND Brewery.BreweryId = ItemIndex.BreweryId AND Brewery.LanguageId = 1 AND UserItemJournal.PersonalRating IS NOT NULL;

android sqlite select avg multiple columns

I have a problem with a query of sqlite. I want to do is the average of two columns and place the result in a new column. example:
id | max_ma | max_ta | avg_max (new column)
1 | 100 | 102 | 101 ==> (100+102)/2
2 | 100 | null | 100 ==> (100+0)/1 INGNORE NULL
wrong code:
public Cursor list() {
String lista_ge = "SELECT *, AVG(tr_max_m + tr_max_t) AS media_max FROM bdt_registro ORDER BY tr_fecha DESC, _id DESC";
return db.rawQuery(lista_ge, null);
}
or
public Cursor list() {
String lista_ge = "SELECT *, ((tr_max_m + tr_max_t)/COUNT(*)) AS media_max FROM bdt_registro ORDER BY tr_fecha DESC, _id DESC";
return db.rawQuery(lista_ge, null);
}
Thanks for your interest
Use the COALESCE function:
SELECT *,
(COALESCE(tr_max_m, tr_max_t, 0) + COALESCE(tr_max_t, tr_max_m, 0)) / 2 AS media_max
FROM
bdt_registro ORDER BY tr_fecha DESC, _id DESC
If both are non_NULL you get: (tr_max_t + tr_max_m) / 2
If tr_max_m is NULL you get: (tr_max_t + tr_max_t) / 2 = tr_max_t
If tr_max_t is NULL you get: (tr_max_m + tr_max_m) / 2 = tr_max_m
If both are NULL you get: (0 + 0) / 2 = 0
Probably the best approach will be to use a lot of cases:
SELECT max_ma, max_ta,
CASE WHEN max_ma IS NULL THEN
CASE WHEN max_ta IS NULL THEN 0
ELSE max_ta END
ELSE
CASE WHEN max_ta IS NULL THEN max_ma
ELSE (max_ma + max_ta) / 2 END
END avg_max
FROM user_address
Fiddle here.
By the way, I noticed you were trying to use AVG. If you're wondering how you could have used that function then this will give you an idea. It won't be faster than the previous approach because it adds more calculation, though:
SELECT id,
max(CASE WHEN kind = 1 THEN aMax END) max_ma,
max(CASE WHEN kind = 2 THEN aMax END) max_ta,
avg(aMax) aMax
FROM (
SELECT id, max_ma aMax, 1 kind FROM user_address
UNION ALL
SELECT id, max_ta, 2 FROM user_address
) s
GROUP BY id
Another solution based on function IFNULL and...
... "un-aggregated" aggregated function COUNT:
SELECT *,
(IFNULL(max_ma, 0)+IFNULL(max_ta, 0))/(COUNT(max_ma)+COUNT(max_ta)) AS avg_max
FROM bdt_registro
GROUP BY id;
... CASE:
SELECT *,
(IFNULL(max_ma, 0)+IFNULL(max_ta, 0)) / (CASE WHEN max_ma IS NULL THEN 0 ELSE 1 END + CASE WHEN max_ta IS NULL THEN 0 ELSE 1 END) AS avg_max
FROM bdt_registro;
ยป SQL Fiddle

Getting the type of a column in SQLite

My mate made the database for my Android app. For example, one of the tables is created like this:
CREATE TABLE table1(
id_fields_starring INTEGER PRIMARY KEY AUTOINCREMENT
, fields_descriptor_id INTEGER NOT NULL
, starring_id INTEGER NOT NULL
, form_mandatory INTEGER NOT NULL DEFAULT 1
, form_visible INTEGER NOT NULL DEFAULT 1
, FOREIGN KEY(fields_descriptor_id) REFERENCES pr_fields_descriptor(id_fields_descriptor) ON DELETE CASCADE ON UPDATE CASCADE
, FOREIGN KEY(starring_id) REFERENCES starring(id_starring) ON DELETE CASCADE ON UPDATE CASCADE
)
From the fields of a table I need to know which of them is INTEGER NOT NULL and which is INTEGER NOT NULL DEFAULT 1, because for the first case I must create dinamically an EditText and for the second case I must create a CheckBox. Any idea?
Calling:
PRAGMA table_info(table1);
will dump the table information, e.g.
cid|name |type |notnull |dflt_value |pk
0 |id_fields_starring |INTEGER |0 | |1
1 |fields_descriptor_id |INTEGER |1 | |0
2 |starring_id |INTEGER |1 | |0
3 |form_mandatory |INTEGER |1 |1 |0
4 |form_visible |INTEGER |1 |1 |0
and simply find the row in the cursor with notnull=1 and dflt_value=1
Edit:
To list all columns defined as INTEGER NOT NULL DEFAULT 1 this would work (helper is your instance of SQLiteOpenHelper):
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = db.rawQuery("PRAGMA table_info(table1)", null);
try {
int nameIdx = cursor.getColumnIndexOrThrow("name");
int typeIdx = cursor.getColumnIndexOrThrow("type");
int notNullIdx = cursor.getColumnIndexOrThrow("notnull");
int dfltValueIdx = cursor.getColumnIndexOrThrow("dflt_value");
ArrayList<String> integerDefault1NotNull = new ArrayList<String>();
while (cursor.moveToNext()) {
String type = cursor.getString(typeIdx);
if ("INTEGER".equals(type)) {
// Integer column
if (cursor.getInt(notNullIdx) == 1) {
// NOT NULL
String defaultValue = cursor.getString(dfltValueIdx);
if ("1".equals(defaultValue)) {
integerDefault1NotNull.add(cursor.getString(nameIdx));
}
}
}
}
System.out.println("integerDefault1NotNull now contains a list of all columns " +
" defined as INTEGER NOT NULL DEFAULT 1, " + integerDefault1NotNull);
} finally {
cursor.close();
}
For those of you looking to do this programmatically in Xamarin perhaps (like me) you can execute the PRAGMA query against a SQLiteConnection as follows:
connection.Query<SqliteColumninfo>($"PRAGMA table_info({tableName})");
I couldn't find an object defined in the PCL Sqlite package for collecting the result so I created a class to collect the aforementioned components:
public class SqliteColumninfo
{
public string Cid { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public int NotNull { get; set; }
public int Dflt_Value { get; set; }
public int PK { get; set; }
}
Here's a query that I use to get table / column / data type / primary key info for all tables and views:
SELECT m.name AS table_name, UPPER(m.type) AS table_type,
p.name AS column_name, p.type AS data_type,
CASE p.pk WHEN 1 THEN 'PRIMARY KEY' END AS const
FROM sqlite_master AS m
INNER JOIN pragma_table_info(m.name) AS p
WHERE m.name NOT IN ('sqlite_sequence')
ORDER BY m.name, p.cid
This command returns a list of dictionaries which contain column name and datatype:
db.execute("SELECT name, type FROM PRAGMA_TABLE_INFO(?);", table1)
This command return columns datatype:
db.execute("SELECT type FROM PRAGMA_TABLE_INFO(?);", table1)

Anyway to change row number in SQLITE rawquery?

I've got the following cursor set up to fill a dialog box with a users payment history
Cursor PaymentsCursor = db.getReadableDatabase().rawQuery(
"SELECT _id, Date, Payment FROM tblPaymentHistory WHERE DebtName = '"
+ debtname + "'" + "ORDER BY _id ASC", null);
SimpleCursorAdapter HistoryAdapter = new SimpleCursorAdapter(this,
R.layout.paymenthistoryrow, PaymentsCursor, from, to);
The problem is though that if there is more than one type of debt, and payments are made to each debt out of order, when the payment history returns it's results, it returns as out-of-order row numbers, for example 1,2,6,7,9,12,etc. I know it's pulling the _id (unique key) from the database, but is there a way to re-base or change the row number in the query, so that each result returns as "1,2,3,4,5,etc" regardless of original ID?
I thought that the ORDER BY _id or even ORDER BY Date ASC would fix this but it didn't.
My rows in the database look something like this:
1, TEST, 4/13/2012, 250
2, TEST, 4/13/2012, 300
3, TEST, 4/14/2012, 222
4, TEST2, 4/14/2012, 500
5, TEST, 4/15/2012, 600
When the user clicks history for "TEST", it returns back as 1,2,3,5... and if they pull up history for "TEST2", it shows as "4", I'm trying to get it so TEST shows "1,2,3,4" and TEST2 shows "1"
Damn I can't answer my own answer, but here's what I ended up doing:
Thanks guys. I found an alternate option that modified the view, so as not having to touch the SqLite db. heres the link that i foundModifying SimpleCursorAdapter's data
And here is the result:
PaymentsCursor = db.getReadableDatabase().rawQuery(
" SELECT _id, Date, Payment FROM tblPaymentHistory WHERE DebtName = '"
+ debtname + "'" + "ORDER BY _id ASC", null);
String[] from = new String[] { DbAdapter.KEY_HISTORY_ID,
DbAdapter.HISTORY_DATE, DbAdapter.HISTORY_PAYMENT };
int[] to = new int[] { R.id.PAYMENTNO, R.id.PAYMENTDATE,
R.id.PAYMENTAMOUNT };
SimpleCursorAdapter HistoryAdapter = new SimpleCursorAdapter(this,
R.layout.paymenthistoryrow, PaymentsCursor, from, to);
HistoryAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int column) {
if (column == 0) { // let's suppose that the column 0 is the
// date
TextView tv = (TextView) view;
String rownum = String.valueOf(cursor.getPosition() + 1);
// here you use SimpleDateFormat to bla blah blah
tv.setText(rownum);
return true;
}
return false;
}
});
paymenthistory.setAdapter(HistoryAdapter);
It may not be the most glamourous way, but now each time the window comes up with the history, it's using the row number (plus one) to indicate which # it is.
Thanks all!
Here is one way to get the "re-based" ids. In this example, the "new ids" are based on the grade (i.e. the "old ids" in your case):
.headers on
create table foo (name text, grade int);
insert into foo values ('Joe', 45);
insert into foo values ('Anna', 98);
insert into foo values ('Julie', 78);
select name,
grade,
(select count(*) from foo t1 where t1.grade>=t2.grade) as rank
from foo t2;
select name,
grade,
(select count(*) from foo t1 where t1.grade>=t2.grade) as rank
from foo t2
order by rank;
Having saved this as foo.sql, I get this:
[someone#somewhere tmp]$ sqlite3 < foo.sql
name|grade|rank
Joe|45|3
Anna|98|1
Julie|78|2
name|grade|rank
Anna|98|1
Julie|78|2
Joe|45|3
I've played a bit with what #sixfeetsix answered and since that fails to give 1,2,3,4,.. numbering in combination with the WHERE you might need to put in more subqueries (maybe not but, I'm not that good with queries):
SELECT (
SELECT count( * ) + 1
FROM (
SELECT *
FROM tblPaymentHistory
WHERE DebtName = ?
)
AS t1
WHERE t1._id < t2._id
)
AS _id,
Date,
Payment
FROM tblPaymentHistory AS t2
WHERE DebtName = ?
ORDER BY _id;
put in java String and leave the ? in there to get escaped values (injection safe):
Cursor PaymentsCursor = db.getReadableDatabase().rawQuery(
"...WHERE DebtName=? .. WHERE DebtName=? .. ", new String[]{ debtname, debtname });
SimpleCursorAdapter HistoryAdapter = new SimpleCursorAdapter(this,
R.layout.paymenthistoryrow, PaymentsCursor, from, to);
This query worked for me after long research...
Empirical results I derived :
1)You need to define where condition in sub-query also.
2)if ids (t2._id <= t1._id) compared in relation operator will be primary keys then it
will work fine in all cases.
3)Regarding orderby condition you have to decide that according to your choice or need.
SELECT
(SELECT COUNT(*) FROM table t2 WHERE t2._id <= t1._id AND t2.Recipe_id = 2) AS RowNumber,_id,Recipe_id,col2,col3,col4
FROM table t1
WHERE Recipe_id = 2
ORDER BY _id
How it works:-
Say we have a sequence of primary keys 1,2,3,4,5,6 in some table t
Now we create two aliases of it using table t1 and table t2
Now both have same sequence table t1 -> 11,12,13,14,15,16
table t2 -> 11,12,13,14,15,16
now this condition ( WHERE t2._id <= t1._id ) compares first primary key "11" of t2 with
the first primary key "11" of t2 as 11=11 it will return count() that only one row exists, hence we get "1" in row number..
*** remember for every row in Outer query the sub-query is executed ***
Hence now outer query is at row second having primary key "12"
now it will again compare the ( WHERE t2._id <= t1._id ) this time again t2._id contains "11" while t1._id contains "12"..
Quiet clear it will return that TWO rows are there which are having ids <= 12 that is 11 and 12
this way it will generate the desired sequence.........
This is a simple trick to generate the sequence.. not simple actually in one look but really simple when you get into depth of it..
I am not expert but this is what i understood..
Hope the explanation helps...
As there are various solutions or same solutions available on net but no explanation..
:)

Categories

Resources