Outer Join in Google Sheets - arrays

I have 2 sets of data that I want to do an outer join on (basically include all data from both sets, with empty cells where data is in one set but not the other)
I've looked at
Join tables in google sheet - full join
and
Google Sheets outer join on 2 tables that get summarised
but I just can't get mine to work.
I've simplified my data till I can get this working, but basically both datasets have year and month (which I combine to make an ID, year and month only appears once in each dataset.
My output should look like the Dataset highlighted in green (I typed out manually)
But all I get from my formula is either an Error (Like in the 1st screenshot)
Or a load of #VALUE!
This is a link to my sample sheet.
https://docs.google.com/spreadsheets/d/1Iyhi7WKAA6g0hWpgl33fOe8q78MtzsATq4khnqcAT-w/edit?usp=sharing
It's driving me insane, as I don't fully understand how the array formula is working

Try this:
=arrayformula(
unique(
{
B2:E5,
iferror(
vlookup(
A2:A5,
G2:K5,
column(J2:K5) - column(G2) + 1,
false
)
);
H3:I5,
iferror(
vlookup(
G3:G5,
A3:E5,
column(D3:E5) - column(A3) + 1,
false
)
),
J3:K5
}
)
)
If you need more functionality, the Formulas by Top Contributors add-on includes SQL join functions.

The Google QUERY() function is very powerful and modeled after SQL but it is not a full implementation. So, I decided to write a custom function to simulate SQL JOINs, Inner, Left, Right and Full.
const ss = SpreadsheetApp.getActiveSpreadsheet();
/**
* Combines two ranges with a common key and can be used standalone or with the QUERY() function to simulate joins.
*
*
* #constructor
* #param {(string|array)} range1 - the main table as a named range, a1Notation or an array
* #param {(string|array)} range2 - the related table as a named range, a1Notation or an array
* #param {number} primaryKey - the unique identifier for the main table, columns start with "1"
* #param {number} foreignKey - the key in the related table to join to the main table, columns start with "1"
* #param {string} joinType, type of join - "Inner", "Left", "Right", "Full", optional and defaults to "Inner", case insensitive
* #returns {array} array results as a two dimensional array
* #customfunction
*
* Result Set Example:
*
* =QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio' AND Col8=2", FALSE)
*
* |EmpID|LastName|FirstName|OrderID|CustomerID|EmpID|OrderDate|ShipperID|
* |:----|:-------|:--------|:------|:---------|:----|:--------|:--------|
* |1 |Davolio |Nancy |10285 |63 |1 |8/20/1996|2 |
* |1 |Davolio |Nancy |10292 |81 |1 |8/28/1996|2 |
* |1 |Davolio |Nancy |10304 |80 |1 |9/12/1996|2 |
* etc.
*
* Other Examples:
* =denormalize("Employees","Orders",1,3)
* =denormalize("Employees","Orders",1,3,"full")
* =QUERY(denormalize("Employees","Orders",1,3,"left"), "SELECT * ", FALSE)
* =QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio'", FALSE)
* =QUERY(denormalize("Employees","Orders",1,3), "SELECT * WHERE Col2 = 'Davolio' AND Col8=2", FALSE)
* =denormalize("Orders","OrderDetails",1,2)
* // multiple joins
* =denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3)
* =QUERY(denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3), "SELECT *", FALSE)
* =denormalize(denormalize("Employees","Orders",1,3),"OrderDetails",1,2)
* =QUERY(denormalize("Employees",denormalize("Orders","OrderDetails",1,2),1,3), "SELECT *", FALSE)
* =QUERY(denormalize(denormalize("Employees","Orders",1,3),"OrderDetails",4,2), "SELECT *", FALSE)
*
* Joins Types:
* (INNER) JOIN: Returns records that have matching values in both tables
* LEFT (OUTER) JOIN: Returns all records from the left table, and the matched records from the right table
* RIGHT (OUTER) JOIN: Returns all records from the right table, and the matched records from the left table
* FULL (OUTER) JOIN: Returns all records when there is a match in either left or right table
*
* Note: the most common join is INNER which is why that is the default join type
*
* General:
* This alogithm is more efficient than using nested loops and uses a form of a hash table instead.
* A hash table is a structure that can map index keys to values and typically resembles something like this:
* [index][values]
*
* Since javascript provides a native function to return the index, there is no need to store it so,
* this hash table only stores the values.
*
* There is minimal testing in DENORMALIZE() to validate parameters.
*
* Author/Coder/Tester: John Agusta, 03/28/2021, Raleigh, NC USA
*
* License: Follows the GNU General Public License (GNU GPL or simply GPL), a series of widely-used free
* software licenses that guarantee end users the freedom to run, study, share, and modify the software.
*
* http://www.gnu.org/licenses/gpl.html
*
*
* Note: DENORMALIZE() can simulate multiple joins by nesting DENORMALIZE() functions as needed.
*
* Recursion is theoretically possible to unlimited depth, although only a few levels are normally used in practical programs
* as performance will degrade accordingly.
*
* DENORMALIZE(range1, range2, primaryKey, foreignKey, joinType)
*
*/
function DENORMALIZE(range1, range2, primaryKey, foreignKey, joinType) {
var i = 0;
var j = 0;
var index = -1;
var lFound = false;
var aDenorm = [];
var hashtable = [];
var aRange1 = "";
var aRange2 = "";
joinType = DefaultTo(joinType, "INNER").toUpperCase();
// the 6 lines below are used for debugging
//range1 = "Employees";
//range1 = "Employees!A2:C12";
//range2 = "Orders";
//primaryKey = 1;
//foreignKey = 3;
//joinType = "LEFT";
// Sheets starts numbering columns starting with "1", arrays are zero-based
primaryKey -= 1;
foreignKey -= 1;
// check if range is not an array
if (typeof range1 !== 'object') {
// Determine if range is a1Notation and load data into an array
if (range1.indexOf(":") !== -1) {
aRange1 = ss.getRange(range1).getValues();
} else {
aRange1 = ss.getRangeByName(range1).getValues();
}
} else {
aRange1 = range1;
}
if (typeof range2 !== 'object') {
if (range2.indexOf(":") !== -1) {
aRange2 = ss.getRange(range2).getValues();
} else {
aRange2 = ss.getRangeByName(range2).getValues();
}
} else {
aRange2 = range2;
}
// make similar structured temp arrays with NULL elements
var tArray1 = MakeArray(aRange1[0].length);
var tArray2 = MakeArray(aRange2[0].length);
var lenRange1 = aRange1.length;
var lenRange2 = aRange2.length;
hashtable = getHT(aRange1, lenRange1, primaryKey);
for(i = 0; i < lenRange2; i++) {
index = hashtable.indexOf(aRange2[i][foreignKey]);
if (index !== -1) {
aDenorm.push(aRange1[index].concat(aRange2[i]));
}
}
// add left and full no matches
if (joinType == "LEFT" || joinType == "FULL") {
for(i = 0; i < lenRange1; i++) {
index = aDenorm.indexOf(aRange1[i][primaryKey]);
//index = aScan(aDenorm, aRange1[i][primaryKey], primaryKey)
if (index == -1) {
aDenorm.push(aRange1[i].concat(tArray2));
}
}
}
// add right and full no matches
if (joinType == "RIGHT" || joinType == "FULL") {
for(i = 0; i < lenRange2; i++) {
index = ASCAN(aDenorm, aRange2[i][foreignKey], primaryKey)
if (index == -1) {
aDenorm.push(tArray1.concat(aRange2[i]));
}
}
}
return aDenorm;
}
function getHT(aRange, lenRange, key){
var aHashtable = [];
var i = 0;
for (i=0; i < lenRange; i++ ) {
//aHashtable.push([aRange[i][key], i]);
aHashtable.push(aRange[i][key]);
}
return aHashtable;
}
function MakeArray(length) {
var i = 0;
var retArray = [];
for (i=0; i < length; i++) {
retArray.push("");
}
return retArray;
}
function DefaultTo(valueToCheck, valueToDefault) {
return typeof valueToCheck === "undefined" ? valueToDefault : valueToCheck;
}
/**
*
* Search a multi-dimensional array for a value and return either the index or value if found, -1 or an empty sting otherwise
* #constructor
* #param {array} aValues - the array to scan
* #param {string} searchVal - the value to look for
* #param {number} searchCol - the array column to search
* #param {number} returnCol - optional, the array column to return if specified, otherwise array index is returned
* #returns {(number|value)} array index of value found or array value specified by returnCol
* #customfunction
*/
function ASCAN(aValues, searchVal, searchCol, returnCol) {
var retval = typeof returnCol === "undefined" ? -1 : "";
var i = 0;
var aLen = aValues.length;
for (i = 0; i < aLen; i++) {
if (aValues[i][searchCol] == searchVal) {
retval = typeof returnCol === "undefined" ? i : aValues[i][returnCol];
break;
}
}
return retval;
}
I have a sheet with examples here:
https://script.google.com/home/projects/1aQDY3Y0rOj0VrViLffYfARP9rp2j9jQ0XpUcFvye8XnxvkHy3Qr6_d0_/edit

Related

What are the options available to get Primary Key Column Names in Snowflake?

I need to fetch all Primary Keys, their parent Table Name , Column Name, and Schema Name together.
I am using INFORMATION_SCHEMA for all metadata fetching, SHOW PRIMARY KEYS/DESCRIBE TABLE does the job but it's not an option here.
Need something similar to SELECT *FROM DB.INFORMATION_SCHEMA.XXX.
What are the options we have here?
*I am Using JDBC
You may consider using: getPrimaryKeys(String, String, String)
Details: https://docs.snowflake.com/en/user-guide/jdbc-api.html#object-databasemetadata
A while back, I wrote a user defined table function (UDTF) to get the PK column(s) for a single table, each column in the PK as a single row in the return. I extended it to return a table with all the columns in PKs in an entire database.
Once you create the UDTF, you can get all the PKs for a database like this:
select * from table(get_pk_columns(get_ddl('database', 'MY_DB_NAME')));
It will return a table with columns for the schema name, table name, and column name(s). Note that if there's a composite PK, it shows in the table as one row per column. You can of course use an aggregate function such as listagg() to change that into a single row with the columns names of the composite PK separated by commas.
It's possible that if you have a very large number of tables/columns in your database, the return of the GET_DDL() function will be too large to fit into the 16mb limit. If it does fit, this should return the results quickly.
/********************************************************************************************************
* *
* User defined table function (UDTF) to get all primary keys for a database. *
* *
* #param {string}: DATABASE_DDL The DDL for the database to get the PKs. Usually use GET_DDL() *
* #return {table}: A table with the columns comprising the table's primary key *
* *
********************************************************************************************************/
create or replace function GET_PK_COLUMNS(DATABASE_DDL string)
returns table ("SCHEMA_NAME" string, "TABLE_NAME" string, PK_COLUMN string)
language javascript
as
$$
{
processRow: function get_params(row, rowWriter, context){
var startTableLine = -1;
var endTableLine = -1;
var dbDDL = row.DATABASE_DDL.replace(/'[\s\S]*'/gm, '')
var lines = dbDDL.split("\n");
var currentSchema = "";
var currentTable = "";
var ln = 0;
var tableDDL = "";
var pkCols = null;
var c = 0;
for (var i=0; i < lines.length; i++) {
if (lines[i].match(/^create .* schema /)) {
currentSchema = lines[i].split("schema")[1].replace(/;/, '');
//rowWriter.writeRow({PK_COLUMN: "currentSchema = " + currentSchema});
}
if (lines[i].match(/^create or replace TABLE /)) {
startTableLine = i;
}
if (startTableLine != -1 && lines[i] == ");") {
endTableLine = i;
}
if (startTableLine != -1 && endTableLine != -1) {
// We found a table. Now, join it and send it for parsing
tableDDL = "";
for (ln = startTableLine; ln <= endTableLine; ln++) {
if (ln > 0) tableDDL += "\n";
tableDDL += lines[ln];
}
startTableLine = -1;
endTableLine = -1;
currentTable = getTableName(tableDDL);
pkCols = getPKs(tableDDL);
for (c = 0; c < pkCols.length; c++) {
rowWriter.writeRow({PK_COLUMN: pkCols[c], SCHEMA_NAME: currentSchema, TABLE_NAME: currentTable});
}
}
}
function getTableName(tableDDL) {
var lines = tableDDL.split("\n");
var s = lines[1];
s = s.substring(s.indexOf(" TABLE ") + " TABLE ".length);
s = s.split(" (")[0];
return s;
}
function getPKs(tableDDL) {
var c;
var keyword = "primary key";
var ins = -1;
var s = tableDDL.split("\n");
for (var i = 0; i < s.length; i++) {
ins = s[i].indexOf(keyword);
if (ins != -1) {
var colList = s[i].substring(ins + keyword.length);
colList = colList.replace("(", "");
colList = colList.replace(")", "");
var colArray = colList.split(",");
for (pkc = 0; c < colArray.length; pkc++) {
colArray[pkc] = colArray[pkc].trim();
}
return colArray;
}
}
return []; // No PK
}
}
}
$$;
I did this using a very simple SQL based UDTF:
CREATE OR REPLACE FUNCTION admin.get_primary_key(p_table_nm VARCHAR)
RETURNS TABLE(column_name VARCHAR, ordinal_position int)
AS
WITH t AS (select get_ddl('TABLE', p_table_nm) tbl_ddl)
, t1 AS (
SELECT POSITION('primary key (', tbl_ddl) + 13 pos
, SUBSTR(tbl_ddl, pos, POSITION(')', tbl_ddl, pos) - pos ) str
FROM t
)
SELECT x.value column_name
, x.index ordinal_position
FROM t1
, LATERAL SPLIT_TO_TABLE(t1.str, ',') x
;
You can then query this in a SQL statement:
select *
FROM TABLE(admin.get_primary_key('<your table name>'));
Unfortunately, due to the odd implementation of GET_DDL(), it will only accept a string literal and you can't use this function with a lateral join to information_schema.tables. Get the following error:
SQL compilation error: Invalid value [CORRELATION(T.TABLE_SCHEMA) ||
'.' || CORRELATION(T.TABLE_NAME)] for function '2', parameter
EXPORT_DDL: constant arguments expected

select array value like foreign key way join multiple table

I have table like below
table
CREATE TABLE IF NOT EXISTS "Article"(
"ArticleId" SERIAL NOT NULL,
"GenresIdList" integer[],
...
PRIMARY KEY ("ArticleId")
);
CREATE TABLE IF NOT EXISTS "Tag0"(
"TagId" SERIAL NOT NULL,
"Name" varchar,
...
PRIMARY KEY ("TagId")
);
ArticleId | GenresIdList
1 | {1} |
2 | {1} |
3 | {1,2} |
4 | {1,2,3} |
TagId | Name
1 | hiphop
2 | rock
When user input data inputGenres I want get below result:
if inputGenres = ['hiphop','rock','classical']; then will get no rows in Article
if inputGenres = ['hiphop','rock']; get Article rows 3 and 4
but because I select two table separate then even I use && in select article table when inputGenres = ['hiphop','rock','classical']; when convert to id array I will become [1,2] because there is no classical, then I will get rows 3 and 4.
How to solve this?
ps. I have to design table like this, only store id not store name in 'Article'. so I hope not redesign table
code (with nodejs)
// convert inputGenres to tag0TagIdList
var tag0TagIdList = [];
var db = dbClient;
var query = 'SELECT * FROM "Tag0" WHERE "Name" IN (';
for (var i = 0; i < inputGenres.length; i++) {
if (i > 0) {
query += ',';
}
query += '$' + (i + 1);
}
query += ') ORDER BY "Name" ASC';
var params = inputGenres;
var selectTag0 = yield crudDatabase(db,query,params);
for (var i = 0; i < selectTag0.result.rows.length; i++) {
tag0TagIdList.push(selectTag0.result.rows[i].TagId);
}
// end: convert inputGenres to tag0TagIdList
var db = dbClient;
var query = 'SELECT * FROM "Article" WHERE "GenresIdList" && $1';
var params = [tag0TagIdList];
var selectArticle = yield crudDatabase(db,query,params);
var tag0TagIdList = [];
var db = dbClient;
var query = 'select * from "Article" where "GenresIdList" #> (select array_agg ("TagId") from unnest (array[';
for (var i = 0; i < inputGenres.length; i++) {
if (i > 0) {
query += ',';
}
query += '$' + (i + 1);
}
query += ']) as input_tags left join "Tag0" on ( "Name" = input_tags))';
I don't know java much, but this should return what you want.
query example:
SELECT * FROM "Article"
WHERE
"GenresIdList" #> (
SELECT
array_agg ( "TagId" )
FROM
unnest (ARRAY [ 'hiphop', 'rock' ] ) AS input_tags
LEFT JOIN "Tag0" ON (
"Name" = input_tags ) )

minus the quantity value in the database by $_POST in yii

I have a table called Apparatus and there is a column there called "quantity",
I have another table called Transaction where you can select the apparatus id and reserve a quantity
how can i minus the quantity in the transaction from the quantity in apparatus? I'm getting undefined index: id error...
here is a code in my controller:
if (isset($_POST['Transaction']))
{
$transaction->attributes = $_POST['Transaction'];
$transaction->save();
$apparatus = Apparatus::model()->findByPk($_POST['id']);
$apparatus->quantity = $apparatus->quantity - ($_POST['quantity']);
$apparatus->save();
Before doing arithematic operation, check whether the index id is present.
I am sure, your $_POST variables does not hold id as index.
So, i suggest to do the following:
if(Yii::app()->request->getPost('id') && Yii::app()->request->getPost('quantity') && Yii::app()->request->getPost('Transaction')){
$id = (int) Yii::app()->request->getPost('id');
$quantity = (int) Yii::app()->request->getPost('quantity');
$apparatus = Apparatus::model()->findByPk($id);
if($apparatus){
$apparatus->quantity = $apparatus->quantity - $quantity;
if($apparatus->validate()){
$apparatus->save();
}else{
// Log
var_dump($apparatus->errors);
}
}
}
Alternatively, you can dump {var_dump($_POST)} the post variables to see whether the $_POST contains id as index.
Here's what I did: (controller)
if (isset($_POST['Transaction']))
{
$transaction->attributes = $_POST['Transaction'];
$transaction->save();
$id=$transaction->apparatus_id=($transaction['apparatus_id']);
$quantity=$transaction->quantity=($transaction['quantity']);
$apparatus = Apparatus::model()->findByPk($id);
$apparatus->quantity = $apparatus->quantity - $quantity;
$apparatus->save();
}

How to use IN Clause for list of strings or GUID's in Dapper

I am trying to write a dapper query for IN clause, but it's not working throwing casting error saying "Conversion failed when converting the nvarchar value 'A8B08B50-2930-42DC-9DAA-776AC7810A0A' to data type int." . In below query fleetAsset is Guid converted into string.
public IQueryable<MarketTransaction> GetMarketTransactions(int fleetId, int userId, int rowCount)
{
//Original EF queries which I am trying to convert to Dapper
//var fleetAsset = (from logicalFleetNode in _context.LogicalFleetNodes
// where logicalFleetNode.LogicalFleetId == fleetId
// select logicalFleetNode.AssetID).ToList();
////This query fetches guid of assetprofiles for which user having permissions based on the assets user looking onto fleet
//var assetProfileIds = (from ap in _context.AssetProfileJoinAccounts
// where fleetAsset.Contains(ap.AssetProfile.AssetID) && ap.AccountId == userId
// select ap.AssetProfileId).ToList();
var fleetAsset = _context.Database.Connection.Query<string>("SELECT CONVERT(varchar(36),AssetID) from LogicalFleetNodes Where LogicalFleetId=#Fleetid",
new { fleetId }).AsEnumerable();
//This query fetches guid of assetprofiles for which user having permissions based on the assets user looking onto fleet
var sql = String.Format("SELECT TOP(#RowCount) AssetProfileId FROM [AssetProfileJoinAccounts] AS APJA WHERE ( EXISTS (SELECT " +
"1 AS [C1] FROM [dbo].[LogicalFleetNodes] AS LFN " +
"INNER JOIN [dbo].[AssetProfile] AS AP ON [LFN].[AssetID] = [AP].[AssetID]" +
" WHERE ([APJA].[AssetProfileId] = [AP].[ID]) " +
" AND ([APJA].[AccountId] = #AccountId AND LogicalFleetId IN #FleetId)))");
var assetProfileIds = _context.Database.Connection.Query<Guid>(sql, new { AccountId = userId, FleetId = fleetAsset, RowCount=rowCount });
Dapper performs expansion, so if the data types match, you should just need to do:
LogicalFleetId IN #FleetId
(note no parentheses)
Passing in a FleetId (typically via an anonymous type like in the question) that is an obvious array or list or similar.
If it isn't working when you remove the parentheses, then there are two questions to ask:
what is the column type of LocalFleetId?
what is the declared type of the local variable fleetAsset (that you are passing in as FleetId)?
Update: test case showing it working fine:
public void GuidIn_SO_24177902()
{
// invent and populate
Guid a = Guid.NewGuid(), b = Guid.NewGuid(),
c = Guid.NewGuid(), d = Guid.NewGuid();
connection.Execute("create table #foo (i int, g uniqueidentifier)");
connection.Execute("insert #foo(i,g) values(#i,#g)",
new[] { new { i = 1, g = a }, new { i = 2, g = b },
new { i = 3, g = c },new { i = 4, g = d }});
// check that rows 2&3 yield guids b&c
var guids = connection.Query<Guid>("select g from #foo where i in (2,3)")
.ToArray();
guids.Length.Equals(2);
guids.Contains(a).Equals(false);
guids.Contains(b).Equals(true);
guids.Contains(c).Equals(true);
guids.Contains(d).Equals(false);
// in query on the guids
var rows = connection.Query(
"select * from #foo where g in #guids order by i", new { guids })
.Select(row => new { i = (int)row.i, g = (Guid)row.g }).ToArray();
rows.Length.Equals(2);
rows[0].i.Equals(2);
rows[0].g.Equals(b);
rows[1].i.Equals(3);
rows[1].g.Equals(c);
}

setSelection on Spinner based on rowId

I have a Spinner View that's populated through a SimpleCursorAdapter.
Based on the selection I need to save the rowid in the entry database (position won't work because things can be added and deleted from the Spinner Database).
This I can do by using spinner.getAdapter().getItemId(pos);. But When I edit an entry I need to make the Spinner position selected that is associated with this rowid (currently).
spinner.setSelection(position); won't work because I have the rowid, I need a way to find the current position of the item in the current spinner based on the rowid in the database.
If you want to set the selection of a Spinner thats backed by a CursorAdapter, you can loop through all the items in the Cursor and look for the one you want (assuming that the primary key in your table is named "_id"):
Spinner spinner = (Spinner) findViewById(R.id.spinner);
spinner.setAdapter(new SimpleCursorAdapter(...));
for (int i = 0; i < spinner.getCount(); i++) {
Cursor value = (Cursor) spinner.getItemAtPosition(i);
long id = value.getLong(value.getColumnIndex("_id"));
if (id == rowid) {
spinner.setSelection(i);
}
}
If you want to get the rowid of a selected item, you can do something similar:
Cursor cursor = (Cursor) spinner.getSelectedItem();
long rowid = cursor.getLong(cursor.getColumnIndex("_id"));
There might be a quicker way to do it, but that's always worked for me.
Had an idea when writing this, made a hashtable with rowid->pos when populating the spinner and then used that. Might help someone if they're searching.
I agree with Erich Douglass's above answer but i found fastest loop syntax which will be useful while spinner.getCount() is greater than 50k to 100k.
/* 1 (fastest) */
for (int i = initializer; i >= 0; i--) { ... }
/* 2 */
int limit = calculateLoopLimit();
for (int i = 0; i < limit; i++) { ... }
/* 3 */
Type[] array = getMyArray();
for (Type obj : array) { ... }
/* 4 */
for (int i = 0; i < array.length; i++) { ... }
/* 5 */
for (int i = 0; i < this.var; i++) { ... }
/* 6 */
for (int i = 0; i < obj.size(); i++) { ... }
/* 7 (slowest) */
Iterable<Type> list = getMyList();
for (Type obj : list) { ... }
So i think we can use here second for better performance:
int spinnerCount = spinner.getCount();
for (int i = 0; i < spinnerCount; i++) {
Cursor value = (Cursor) spinner.getItemAtPosition(i);
long id = value.getLong(value.getColumnIndex("_id"));
if (id == rowid) {
spinner.setSelection(i);
}
}
I think that instead of a for loop is better a while, because when you find your item, can break the loop.
int spinnerCount = spinner.getCount();
int i = 0;
while(i++ < spinnerCount) {
Cursor value = (Cursor) spinner.getItemAtPosition(i);
long id = value.getLong(value.getColumnIndex("_id"));
if (id == rowid) {
spinner.setSelection(i);
break;
}
}
First step, create view for your data set, with joins etc.:
CREATE VIEW my_view AS
SELECT _id, field FROM my_table
Second step:
CREATE VIEW my_view2 AS
SELECT count(*) AS row_id, q1.*
FROM my_view AS q1
LEFT JOIN my_view AS q2
WHERE q1._id >= q2._id
GROUP BY q1._id
Then simply:
SELECT * FROM my_view2
Results:
row_id | _id | field
1 4 XbMCmUBFwb
2 6 Te JMejSaK
3 8 dDGMMiuRuh
4 10 phALAbnq c
5 11 EQQwPKksIj
6 12 PAt tbDnf
7 13 f zUSuhvM
8 14 TIMBgAGhkT
9 15 OOcnjKLLER
To get position by id:
SELECT * FROM my_view2 WHERE _id=11
Results:
row_id | _id | field
5 11 EQQwPKksIj
Hope that help
https://stackoverflow.com/a/5040748/1206052
dziobas
has provided an awesome answer.. But I am not surprised why nobody has recommended it.. just want to make some correction in his answer..
CREATE VIEW my_view2 AS
SELECT count(*) AS row_id, q1.*
FROM my_view AS q1
LEFT JOIN my_view AS q2
ON (q1._id >= q2._id)
GROUP BY q1._id
I just replaced "where" with "on" that is required by join..
now u have all the items with their Positions associated with there ID's..
Just assign the ROW_id to the setselection() of spinner
Why do it the hard way when you can do it the right way?
I refer to the manual:
http://d.android.com/reference/android/widget/AdapterView.OnItemSelectedListener.html#onItemSelected%28android.widget.AdapterView%3C?%3E,%20android.view.View,%20int,%20long%29
example code:
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
int index = spinner.getSelectedItemPosition();
Toast.makeText(getBaseContext(),
"You have selected item : " + index + " which is row " + id,
Toast.LENGTH_SHORT).show();
}
public void onNothingSelected(AdapterView<?> arg0) {}
});
Thanks to evancharlton on #android-dev for this enlightment. :)

Resources