I use importrange for combine 2 google sheet Target A and Source B
In sheet A =
Importrange(“linkgooglesheetB”,”SheetB!!A2:M1000)
It’s done
But when data in sheet Source B cleared/changed, then
Data in sheet Target A cleared/changed too
What formula can use same my above formula but data in Target A do not change after combined (A&B), when B changed.
Answer:
You can do this with Google Apps Script.
Explanation:
The IMPORTRANGE formula will always automatically update when the range from which it is importing had a data change. In fact, this is a feature of all sheet formulae; they are designed to keep everything up-to-date when data changes.
For this reason, you can not use a formula. You will have to do this using Google Apps Script.
Example code:
The idea is as such:
When the sheet is edited, a script will run.
If the cell data matches a certain pattern, then the script will paste in the data permanently, in the same way that IMPORTRANGE works.
function runOnEdit(e) {
// We will make the patten here. In this case, the text entered in the cell must be of the form:
// "IMPORT(link,range)
// example:
// IMPORT,https://docs.google.com/spreadsheets/d/some-id/edit, SheetB!A2:M1000
const patternStart = "IMPORT"
const compareVals = e.value.split(",")
if (compareVals.length != 3 || compareVals[0] != patternStart) return
try {
const ss = SpreadsheetApp.openByUrl(compareVals[1])
const importRange = compareVals[2].split('!')
const sheetName = importRange[0]
const range = importRange[1]
const sheet = ss.getSheetByName(sheetName)
console.log(sheetName)
console.log(range)
const data = sheet.getRange(range).getValues()
const formatting = sheet.getRange(range).getTextStyles()
const activeSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const row = e.range.getRow()
const col = e.range.getColumn()
activeSheet.getRange(row, col, data.length, data[0].length).setValues(data).setTextStyles(formatting)
}
catch (err) {
e.range.setValue(err)
}
}
Things to note:
The pattern can not start with an = as this will be read as a formula by Sheets and throw an error.
This must be set up as an installable trigger, as opening a separate Spreadsheet requires authorisation, which can not be done from a simple trigger.
You can install the trigger by clicking on the clock logo in the left toolbar from the Apps Script editor, then clicking + Add Trigger in the bottom right and using the following settings:
Choose which function to run: runOnEdit
Which runs at deployment: Head
Select event source: From spreadsheet
Select event type: On edit
Visual example:
Most of the formula is live update, including query function. However in order not to have your data change for importrange, there is two possible solution:
1)Create a backup sheet by copy paste data from the main sheet, and importrange from the backup sheet, so any change in main sheet will not have impact on your result
2)Write Google App Scrip to copy data, but it take time to learn
Related
I have two spreadsheets, one client facing and one internal facing, that use IMPORTRANGE and QUERY + IMPORTRANGE to display data between one another.
The internal sheet is used to write copies for social media posts, quality check them and give approval. Once approval is given the client sheet needs to update to show the copies on the identically named pages AND in the correct rows.
Approval is given by ticking a checkbox in the internal sheet and the QUERY + IMPORTRANGE formula then pulls that into the client sheet. However, my abilities stop where I have to display this in the correct row position no matter which of the tickboxes are checked.
=iferror(QUERY(IMPORTRANGE("https://docs.google.com/spreadsheets/d/1CIXhcuTigYbggmPjBKpvnH7P-cjBDQfSIcmKN6_0T5M/edit#gid=2018991957","Cloud in FS Survey!C2:N"),"SELECT Col1, Col2, Col3, Col4, Col5, Col6 WHERE Col11 = TRUE"),"No copies approved")
I've seen some discussions of having a second sheet for of the categories (there are 8 in the non-redacted document) and using VLOOKUP to match an ID to the imported row of data, hence I've created 'Row' columns in all tabs on both sheets with the unique row number. However, I want to avoid having hidden tabs in the client facing sheet where they can be revealed by the client.
Any assistance would be appreciated.
Many thanks,
Mark
There are 2 ways to deal with it. If you a add serial number in sheet 1 where approvals are given, you may add another element to your query by adding order by serial no. column ASC
Else you could do the same with order by date column ASC
This way new additions will add below the imported data because they will always be in chronology and not mess up the order of older data.
Having direct importranges where static data exists is always risky.
Alternately, you could also use ID thing without creating an additional hidden sheet, direct vlookups with importrange nested inside the vlookup.
For example, =arrayformula(vlookup(search key, importrangexxx, index, false)
The best solution would be to have a hidden sheet. But all of the above can be decent alternate if you're not dealing with thousands of rows.
If you don't want to use helper sheet or column, you can use Apps Script and onEdit Trigger.
Using onEdit Trigger, you can run a function automatically when a user click the checkbox. Inside that function is a list of commands that will write data to the client side.
Example:
To start with Apps Script:
Go to Tools -> Script Editor.
In your script editor, delete the code in Code.gs
Paste the code provided below.
Click Save.
Code:
function showToClientSide(e) {
var val = e.value;
var range = e.range;
var row = range.getRow();
var col = range.getColumn();
var sheet = range.getSheet();
var clientSS = SpreadsheetApp.openById("Insert Client Sheet ID here")
if(val == "TRUE" && row > 1 && col == 13){ // check if checkbox is checked
var data = sheet.getRange(row, 3, 1, 9).getValues(); //get data from internal
var sh = clientSS.getSheetByName("Cloud in FS Survey"); //client sheet
sh.getRange(row, 3, 1, 9).setValues(data); //write internal data to client
}else if(val == "FALSE" && row > 1 && col == 13){
var sh = clientSS.getSheetByName("Cloud in FS Survey"); //client
sh.getRange(row, 3, 1, 9).clearContent(); //delete data when unchecked
}
}
To setup your Installable Trigger (onEdit):
In the left menu of your Apps Script, click Triggers
Click Add Trigger
Copy the setup below.
Save and Authorize the script.
Testing:
Internal Sheet:
Client Sheet:
Checkbox checked:
Internal:
Client:
Checkbox unchecked:
Internal:
Client:
References:
Installable Triggers
Event Objects
Class Range
Class Sheet
Following this guide, I am able to link data from my Google Sheet into my Google Doc.
https://support.google.com/a/users/answer/9308662?hl=en
Is there a way to have range size update automatically when the table shrinks and grows?
You can do the following:
Create a new named range that includes the current range of the table (ref: Name a range of cells).
Link that named range to your desired location, using Link to data in a spreadsheet.
On your spreadsheet, click Tools > Script editor to open a bound script, and copy the following code. This function retrieves your desired named range and updates it with the current dimensions of your table:
const SHEET_NAME = "Sheet1"; // Change according to your preferences
const NAMED_RANGE_NAME = "MY_RANGE"; // Change according to your preferences
function updateNamedRange() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName(SHEET_NAME);
const namedRanges = ss.getNamedRanges();
const namedRange = namedRanges.find(namedRange => namedRange.getName() === NAMED_RANGE_NAME);
const range = sheet.getRange("A1").getDataRegion();
namedRange.setRange(range);
}
Install a time-driven trigger which will fire updateNamedRange with the specified periodicity. You can do that manually (following these steps), or programatically. In order to install this programmatically, copy and run a function like this once:
function installTimeTrigger() {
ScriptApp.newTrigger("updateNamedRange")
.timeBased()
.everyMinutes(1)
.create();
}
Note:
The previous sample is using everyMinutes with the parameter set to 1, so the range will be updated every minute. You can find methods for alternative frequencies here.
In the sample above, the table is in a sheet named Sheet1, with a named range title MY_RANGE, and it starts at cell A1. Change all those in your spreadsheet if that's not your case.
I'm assuming you already know how to link named ranges.
I'm trying to expand the following formula down a column using an array, which I understand doesn't work exactly with countif. I need this as the divisor in the column where I have the numerator expanding successfully down the column. Here is the function that works fine in each row:
=arrayformula(countifs(unique(if('All Data'!$B$2:$B=$A2,'All Data'!$A$2:$A,)),">="&edate(F$2,0),unique(if('All Data'!$B$2:$B=$A2,'All Data'!$A$2:$A,)),"<="&edate(F$2,1)))
I am trying to avoid copying the formulas all over the sheet, as it is rather large and would prefer it to work as an array where it gives me the count for a2 in row 2, a3 in row 3, a4 in row 4, etc, so I tried this formula, which doesn't work:
=arrayformula(countifs(unique(if('All Data'!$B$2:$B=$A2:$A,'All Data'!$A$2:$A,)),">="&edate(F$2,0),unique(if('All Data'!$B$2:$B=$A2:$A,'All Data'!$A$2:$A,)),"<="&edate(F$2,1)))
Is there a workaround I can use to get this expanding for me?
Thank you!
You could accomplish this with an Apps Script custom function. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project:
function AVERAGE_STUDENTS(classes, monthIndex) {
const sheet = SpreadsheetApp.getActive().getSheetByName("All Data");
const data = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).getValues();
const monthRows = data.filter(row => row[0].getMonth() + 1 == monthIndex);
return classes.map(classy => {
const classRows = monthRows.filter(row => row[1] == classy[0]);
const totalStudents = classRows.length;
const uniqueDates = new Set(classRows.map(element => JSON.stringify([element[0], element[1]]))).size;
return totalStudents / uniqueDates;
});
}
Now, if you go back to your spreadsheet, you can use this function like any in-built one. You just have to provide the appropriate range for the classes (in this case it would be A3:A18) and the month index (for April, that's 4), as you can see here:
Note:
This function could also be modified in order to just call the function at C1 and populate all months automatically.
Reference:
Custom Functions in Google Sheets
I have a google sheet where the cells in the first tab pull data from cells on the second tab.
for example Sheet1 cell A1 has =Sheet2!A1
This is true for every cell on Sheet1
When I do a File - Download As - Microsoft Excel (.xlsx)
It exports the cells with formulas. Is there a way to export the sheets as values and not formulas
In this case, Sheet1 cell A1 would not contain =Sheet2!A1 but the value of =Sheet2!A1?
You can copy your original google spreadsheet and, in the copy, change the formula for the first cell for each tab to import data from the original one:
=IMPORTRANGE("spreadsheet id","'tab name'!range")
Ex:
=IMPORTRANGE("1C-PS4wAHS8ssCNgVDfOsssREAz7PjuQGX23Rk0sssss","'measurement with spaces'!A12:F44")
The ID you can get via original spreadsheet URL:
https://docs.google.com/spreadsheets/d/1C-PS4wAHS8ssCNgVDfOsssREAz7PjuQGX23Rk0sssss/edit#gid=99999999
The exported xlsx file from the copy will have only the values
#fabceolins answer is simple and good for normal scenarios, i noticed however Excel will still contain reference to IMPORTRANGE formula which can cause access issues.
I created Google App script to copy in the following method.
If you can use Google App scripts, add the following functions:
function update_view(dup_id, TL="A1", BR="Z991") {
// Open current Sheet
var ss = SpreadsheetApp.getActiveSpreadsheet()
// Supply a duplicate google doc ID. This document will be exported to excel
var ds = SpreadsheetApp.openById(dup_id)
// UI element for notifying in the google sheets
var ui = SpreadsheetApp.getUi()
//Copy each sheet one by one
var sheets = ss.getSheets();
for (i=0; i<sheets.length; i++) {
src_sheet = sheets[i];
sheet_name = src_sheet.getName();
// If same sheet exists in the destination delete it and create an empty one
dst_sheet = ds.getSheetByName(sheet_name);
if (dst_sheet != null) {
ds.deleteSheet(dst_sheet)
}
dst_sheet = ds.insertSheet(sheet_name);
//set column width correctly
for(j=1; j<=src_sheet.getLastColumn(); j++){
dst_sheet.setColumnWidth(j, src_sheet.getColumnWidth(j))
}
src_range = src_sheet.getRange(TL + ":" + BR);
dst_range = dst_sheet.getRange(TL + ":" + BR);
//Note: DisplayValues is set as Values, formulas are removed in dup sheet
dst_range.setValues(src_range.getDisplayValues());
//Nice to haves for formatting
dst_range.setFontColors(src_range.getFontColors());
dst_range.setFontStyles(src_range.getFontStyles());
dst_range.setBackgrounds(src_range.getBackgrounds());
dst_range.setHorizontalAlignments(src_range.getHorizontalAlignments());
dst_range.setVerticalAlignments(src_range.getVerticalAlignments());
dst_range.setWraps(src_range.getWraps());
dst_contents_range = dst_sheet.getDataRange();
dst_contents_range.setBorder(true, true, true, true, true, true);
}
//Completed copy, Now open the dup document and export.
ui.alert("Backup Complete, Please open " + dup_id + " sheet to view contents.")
}
function update_mydoc_view(){
// https://docs.google.com/spreadsheets/d/<spreadsheet_id>/
update_view("<spreadsheet_id>")
}
To run the function, go to tools->macros->import , import the function and run update_mydoc_view().
After it is completed, export the google sheet into an excel document.
If you want to download a single sheet spreadsheet, instead of download it as .XLSX, download it as .CSV.
If by open the .CSV file by double clicking it shows strange characters, the default encoding of your computer is different from the one used by the Google servers, to fix this do the following:
Open Excel
Click File > Open
Select the .CSV file
The import wizard will be shown. One of the steps will allow you to select the file encoding, select UTF-8.
Once you finish with the import wizard save your file as .XLSX
Related
Microsoft Excel mangles Diacritics in .csv files?
Is it possible to force Excel recognize UTF-8 CSV files automatically?
I did the following and it worked for me :
Duplicate the file
CTRL+A > CTRL+X > CTRL + V Paste Value only (from the paste icon displayed after pressing CTRL+V)
If you want to keep only the values from Sheet1, just select the data in the sheet, copy it, open a new Excel work book and when you paste, rather than using the conventional hotkeys Ctrl+V, right click cell A1 and select Paste values under the Paste options category of the right click menu.
If the problem is that the downloaded Excel does not have functional formulas that take information from your second sheet and instead show the formulas as text, do what the previous commenter said. check your view to make sure you are not in formula view. use the hotkey Ctrl+ ~, or go to the view tab to check your view options.
By default this is what happens - you will see the values not the formula.
Are you sure you are not in formula view in Excel?
If you check "Show formula" it will switch in formula view.
Or generally speaking you can try those:
MS Excel showing the formula in a cell instead of the resulting value
I would be surprised if it was indeed a google sheet problem - it's about Excel display.
I saw several complains about the delay of updating data through IMPORTRANGE in Google Sheets but I need the opposite and don't want the second sheet to get updated automatically, just update at the end of the day for example.
The code is already like this:
=IMPORTRANGE("The Key","The_Page!B:D")
I hacked around this by, on both spreadsheets by creating a refresh loop using a NOW cell, with both spreadsheets crossreferencing each other's NOW cell.
When the original sheet gets appended with a form submission or something, it updates its own NOW cell, and reupdates own IMPORTRANGE cell. The second spreadsheet follows suit, updating its own NOW cell to provide the original sheet with the correct data. Because the second spreadsheet has updated itself, it also updates the main IMPORTRANGE which refreshes the data you want it to display in the first place, as well as the IMPORTRANGE cell which gets the NOW cell from the original spreadsheet
At least, I'm pretty sure that's how it works. All I know, and all I care about, frankly, is that it works
Maybe you need to use the script editor and write a simple function of the kind:
function importData()
{
var ss = SpreadsheetApp.getActiveSpreadsheet(); //source ss
var sheet = ss.getSheetByName("The_Page"); //opens the sheet with your source data
var values = sheet.getRange("B:D").getValues(); //gets needed values
var ts = SpreadsheetApp.openById("The Key"); //target ss - paste your key
ts.getSheetByName("Name of the target sheet").getRange("B:D").setValues(values);
}
And then add a time-driven trigger to this project (Resources > Current project's triggers > Add a new one).