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
I want get all locations around my location but the function ST_Distance_Sphere does not work.
My query:
select *, astext(location) as location from `locations`
where ST_Distance_Sphere(location, POINT(35.905069591297, 49.765869174153)) < 1000
Error :
SQLSTATE[42000]: Syntax error or access violation:
1305 FUNCTION app.ST_Distance_Sphere does not exist (SQL:
select *, astext(location) as location from `locations`
where ST_Distance_Sphere(location, POINT(35.905069591297, 49.765869174153)) < 1000)
For those who still need the function in MariaDB, you can create the function based on the formula
CREATE FUNCTION `st_distance_sphere`(`pt1` POINT, `pt2` POINT) RETURNS
decimal(10,2)
BEGIN
return 6371000 * 2 * ASIN(SQRT(
POWER(SIN((ST_Y(pt2) - ST_Y(pt1)) * pi()/180 / 2),
2) + COS(ST_Y(pt1) * pi()/180 ) * COS(ST_Y(pt2) *
pi()/180) * POWER(SIN((ST_X(pt2) - ST_X(pt1)) *
pi()/180 / 2), 2) ));
END
10.2+
This issue has been fixed and backported with MDEV-13467. It's available 10.2.38, 10.3.29, 10.4.19, 10.5.10
Find their support matrix here.
http://mysql.rjweb.org/doc.php/find_nearest_in_mysql#gcdistdeg
That blog discusses multiple ways of "finding nearest" on the globe in MySQL/MariaDB. As part of that discussion, I developed that Stored Function.
I think that's it ST_DISTANCE https://mariadb.com/kb/en/library/st_distance/
I'm trying to change the default separator to "+"
/**
* #var string $slug
* #Gedmo\Slug(fields={"label"}, separator="+")
* #ORM\Column(length=130, unique=true)
* #Expose
*/
protected $slug;
It doesn't work, I get the following error:
[Symfony\Component\Debug\Exception\ContextErrorException]
Warning: preg_match(): Compilation failed: nothing to repeat at offset 24
When trying to escape the "+" with "\+", I don't have this error any-more but in the database I've got the following:
my\+slug
Same thing with a double escape "\\". Any idea?
Dirty-hack, when desactiving the unique constraint the bug doesn't happen.
* #Gedmo\Slug(fields={"label"}, separator="+", unique=false)
I can't configure datasource in weblogic and get error
Listener refused the connection with the following error: ORA-12505, TNS:listener does not currently know of SID given in connect descriptor
But db's and listener is startup and I can connect to db from SQLDeveloper and from simple java code with jdbc. I install Oracle DB client to server and add this to "PATH", but it don't work.
in listener log
11-ДЕК-2014 17:37:33 * service_update * rtu1 * 0
11-ДЕК-2014 17:38:20 * service_update * nsi * 0
11-ДЕК-2014 17:38:48 * service_update * rtuwh1 * 0
11-ДЕК-2014 17:39:53 * service_update * nsi * 0
11-ДЕК-2014 17:40:23 * (CONNECT_DATA=(SID=RTUARCH)(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__)(USER=fedorov))) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.15.15)(PORT=1245)) * establish * RTUARCH * 0
11-ДЕК-2014 17:40:23 * (CONNECT_DATA=(SID=RTU1)(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__)(USER=fedorov))) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.15.15)(PORT=1246)) * establish * RTU1 * 0
11-ДЕК-2014 17:40:23 * (CONNECT_DATA=(SID=RTU1)(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__)(USER=fedorov))) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.15.15)(PORT=1247)) * establish * RTU1 * 0
11-ДЕК-2014 17:40:23 * (CONNECT_DATA=(SID=RTU1)(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__)(USER=fedorov))) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.15.15)(PORT=1248)) * establish * RTU1 * 0
11-ДЕК-2014 17:40:23 * (CONNECT_DATA=(SID=RTUHW1)(CID=(PROGRAM=JDBC Thin Client)(HOST=__jdbc__)(USER=fedorov))) * (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.15.15)(PORT=1249)) * establish * RTUHW1 * 12505
TNS-12505
Thanks.
As seen your error log, there is problem with your datasource. Double check your datasource. Also there is option of testing a datasource under monitoring tab.
It must give you success result.
I am using Codeigniter 2.1 and Microsoft Sql Server. When I try to insert UTF8 strings in database I get ??? characters. To connect to database I use SqlSrv driver.
Small change in SqlSrv driver solves the problem with unicode.
Just add _make_unicode function code to /system/database/sqlsrv/sqlsrv_driver.php and replace the code for _insert and _update functions
/**
* #param mixed $value
*/
function _make_unicode($value){
if(is_string($value) && $value!="NULL")return "N".$value;
else return $value;
}
/**
* Insert statement
*
* Generates a platform-specific insert string from the supplied data
*
* #access public
* #param string the table name
* #param array the insert keys
* #param array the insert values
* #return string
*/
function _insert($table, $keys, $values)
{
$values_string = "";
for($i=0;$i<count($values);$i++){
if($i==0){
$values_string.= $this->_make_unicode($values[$i]);
}else{
$values_string.= ", ".$this->_make_unicode($values[$i]);
}
}
return "INSERT INTO ".$this->_escape_table($table)." (".implode(', ', $keys).") VALUES ({$values_string})";
}
// --------------------------------------------------------------------
/**
* Update statement
*
* Generates a platform-specific update string from the supplied data
*
* #access public
* #param string the table name
* #param array the update data
* #param array the where clause
* #param array the orderby clause
* #param array the limit clause
* #return string
*/
function _update($table, $values, $where)
{
foreach($values as $key => $val)
{
$valstr[] = $key." = ".$this->_make_unicode($val);
}
return "UPDATE ".$this->_escape_table($table)." SET ".implode(', ', $valstr)." WHERE ".implode(" ", $where);
}
// --------------------------------------------------------------------
Adding to Alex's answer (sorry not enough reputation to comment). Here are the same functions for CodeIgniter 3.1.1.
_insert_batch
/**
* Insert batch statement
*
* Generates a platform-specific insert string from the supplied data.
*
* #param string $table Table name
* #param array $keys INSERT keys
* #param array $values INSERT values
* #return string|bool
*/
protected function _insert_batch($table, $keys, $values)
{
// Multiple-value inserts are only supported as of SQL Server 2008
if (version_compare($this->version(), '10', '>='))
{
foreach($values as &$value) {
$value = $this->_make_unicode($value);
}
return parent::_insert_batch($table, $keys, $values);
}
return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
}
_update
/**
* Update statement
*
* Generates a platform-specific update string from the supplied data
*
* #param string $table
* #param array $values
* #return string
*/
protected function _update($table, $values)
{
foreach ($values as &$value) {
$value = $this->_make_unicode($value);
}
$this->qb_limit = FALSE;
$this->qb_orderby = array();
return parent::_update($table, $values);
}