TL;DR —
Range("A1").Valuelee o escribe una sola celda. Lo que de verdad importa es lo que pasa con muchas celdas:arr = Range("A1:C1000").Valuelee todo el bloque en una matriz Variant 2D de base 1 en una sola llamada, yRange("A1:C1000").Value = arrla reescribe en una sola llamada. Tocar las celdas de una en una —Cells(i, j).Valuedentro de un bucle — cruza de VBA a Excel en cada lectura, y ese cruce de frontera, no la aritmética, es lo que hace lentos a los bucles grandes.
Dim v As Variant
v = Range("A1").Value ' lee una celda en una variable
Range("B1").Value = v * 1.08 ' escribe una celda
Dim arr As Variant
arr = Range("A1:A10000").Value ' lee 10.000 celdas en UNA llamada -> matriz 2D
' ... procesa arr en memoria ...
Range("C1:C10000").Value = arr ' reescribe todas en UNA llamada
Leer y escribir valores es lo primero que hace cualquiera en VBA, y para una sola celda es exactamente
tan sencillo como parece. La idea sobre la que se construye esta guía aparece en cuanto tienes más de una
celda: .Value sobre un rango no es un valor, es un bloque — una matriz bidimensional — y mover ese
bloque en una sola llamada en lugar de celda por celda es la diferencia entre una macro que se siente
instantánea y otra que se queda colgada. Retén eso y tanto las reglas de velocidad como las pequeñas
trampas de tipos encajan solas.
Lo que aprenderás
- El modelo mental —
.Valuees un bloque, y un rango te da una matriz Variant 2D - La regla más importante — lee y escribe el bloque entero en un solo viaje de ida y vuelta, no celda por celda
- Por qué la matriz es de base 1 y 2D, incluso para una sola columna
- El caso especial de una única celda — una celda es un escalar, no una matriz
.Valuees la propiedad por defecto — el atajo, y por qué no conviene apoyarse en él- Asignar un valor reemplaza lo que hubiera en la celda, fórmula incluida
El modelo mental: .Value es un bloque
Para una sola celda, .Value es un único valor que lees o asignas:
Dim name As String
name = Range("A1").Value ' escalar de entrada
Range("A2").Value = "Total" ' escalar de salida
Para un rango de muchas celdas, .Value es toda la cuadrícula de una vez. Léela en un Variant y
obtienes una matriz bidimensional; asígnale una matriz bidimensional y estableces todas las celdas de un
solo movimiento:
Dim block As Variant
block = Range("A1:C100").Value ' block ahora es una matriz Variant de 100 x 3
La palabra importante es una vez. Cada vez que tu código lee o escribe .Value, cruza la frontera
entre el motor de VBA y la aplicación Excel — un salto relativamente caro. Una celda es un salto; un
bloque entero leído en una matriz es también un solo salto. Ese único hecho — un rango es un bloque que
puedes mover en un solo cruce — es lo que gobierna la regla de rendimiento de abajo y es lo más valioso
que hay que saber sobre .Value.
La regla más importante: haz el viaje de ida y vuelta con la matriz, no recorras las celdas
Aquí tienes el mismo trabajo hecho de dos maneras: recorrer una columna, aumentar cada valor un 8% y escribirlo en la columna de al lado.
La forma lenta toca Excel en cada iteración:
Dim i As Long
For i = 1 To 10000
Cells(i, 3).Value = Cells(i, 1).Value * 1.08 ' 2 cruces por fila = 20.000 saltos
Next i
La forma rápida cruza dos veces en total y hace el trabajo en memoria:
Dim src As Variant, out() As Variant, i As Long
src = Range("A1:A10000").Value ' 1 salto de entrada
ReDim out(1 To UBound(src, 1), 1 To 1)
For i = 1 To UBound(src, 1)
out(i, 1) = src(i, 1) * 1.08 ' VBA puro - Excel no interviene
Next i
Range("C1:C10000").Value = out ' 1 salto de salida
La aritmética es idéntica; el bucle tiene la misma longitud. La única diferencia es que la segunda versión lee 10.000 celdas en una llamada y escribe 10.000 en una llamada, mientras que la primera hace 20.000 viajes separados a través de la frontera de VBA a Excel. Con datos reales la versión con matriz suele ser decenas de veces más rápida — a menudo la diferencia entre «instantáneo» y «mira el reloj de arena». Para una ganancia aún mayor, combínala con desactivar el recálculo y la actualización de pantalla durante la escritura; consulta VBA Calculation y VBA ScreenUpdating. El cruce de frontera es el coste, y el viaje de ida y vuelta con la matriz es cómo dejas de pagarlo por celda.
Por qué la matriz es de base 1 y 2D
La matriz Variant que obtienes de un rango sorprende a la gente dos veces. Primero, siempre es de base
1, sin importar el ajuste de Option Base — arr(1, 1) es la celda superior izquierda. Segundo,
siempre es bidimensional, incluso cuando el rango es una sola columna o una sola fila:
Dim col As Variant
col = Range("A1:A5").Value ' una sola columna
Debug.Print col(3, 1) ' NO col(3) - es (fila, columna)
Un rango de una columna es una matriz de 5 por 1, así que lo indexas col(fila, 1); un rango de una fila
es de 1 por 5, indexado fila(1, columna). Escribir col(3) en lugar de col(3, 1) lanza «Subscript out
of range» y es el error más común cuando alguien empieza a usar matrices. Si de verdad quieres una matriz
de 1 dimensión a partir de una sola columna, envuelve la lectura en Application.Transpose — pero ten en
cuenta que Transpose tiene sus propios límites (topa alrededor de 65.536 elementos y fuerza los tipos),
así que para datos grandes o mixtos la matriz 2D simple es más segura. Consulta
VBA Array para trabajar con el resultado.
El caso especial de una única celda
Hay un caso límite que rompe el código de matrices: si el rango es una única celda, .Value devuelve
un escalar simple, no una matriz de 1 por 1.
Dim v As Variant
v = Range("A1").Value ' A1 sola -> un escalar, no v(1, 1)
' v(1, 1) aqui lanza un error
Así que el código que lee rng.Value en una matriz y luego indexa arr(1, 1) funciona con un rng de
varias celdas pero explota cuando rng resulta ser una sola celda. Si el tamaño de un rango puede
reducirse hasta una celda, o bien fuerzas un tamaño mínimo, o bien compruebas rng.Cells.Count > 1 antes
de tratar el resultado como una matriz. Es una pequeña incoherencia en el modelo de objetos de Excel, pero
provoca bugs reales del tipo «funciona en la hoja grande, falla en la pequeña».
.Value es la propiedad por defecto — el atajo y su riesgo
.Value es la propiedad por defecto de un Range, lo que significa que Excel te deja omitirla:
x = Range("A1") ' funciona - .Value implicito
Range("A1") = 42 ' funciona - .Value implicito
Se lee limpio, y mucho código lo hace. El riesgo es que el atajo esconde qué operación quieres decir.
Set rng2 = Range("A1") asigna el objeto rango; x = Range("A1") asigna su valor — la presencia o
ausencia de Set cambia el significado en silencio. Y If Range("A1") = Range("B1") compara valores, lo
que puede ser o no lo que un lector espera cuando el código no dice nada sobre valores. El hábito que vale
la pena mantener: escribe .Value de forma explícita cuando te refieres al valor. Cuesta seis
caracteres y elimina toda una clase de ambigüedad del tipo «¿querían la celda o su contenido?» — sobre todo
alrededor de Set, que se trata en VBA Range.
Asignar un valor reemplaza la fórmula
Una última cosa que hace tropezar a quien mezcla valores y fórmulas: escribir .Value en una celda
sobrescribe lo que hubiera allí, incluida una fórmula.
Range("D2").Formula = "=B2*C2" ' D2 ahora es una formula viva
Range("D2").Value = 100 ' D2 ahora es el numero estatico 100 - la formula desaparecio
Normalmente es lo que quieres — es exactamente como «conviertes una fórmula en su resultado» en una sola
celda (asignas el valor sobre sí mismo). Pero significa que no puedes ajustar un valor sin destruir la
fórmula que lo produjo. Cuando necesitas una fórmula viva en la celda, asigna .Formula, no .Value — las
dos son caras distintas de la misma celda, y se tratan en VBA Formula. Y cuando te
importa el número exacto que lees de vuelta — dinero, fechas, decimales largos — la elección entre
.Value y .Value2 empieza a importar, que es el tema de
VBA Value vs Value2 vs Text.
Cómo ayuda ExcelMaster
Los errores de .Value que cuestan tiempo de verdad no son erratas — son el bucle lento celda por celda en
una hoja grande, el arr(3) que debería haber sido arr(3, 1), el código de matrices que se rompe con un
rango de una sola celda, y el .Value implícito que comparó lo que no era sin avisar. Cada uno se ejecuta;
solo que se ejecuta mal o lento.
ExcelMaster escribe por defecto la
versión rápida y correcta. Pídele «incrementa la columna A un 8% en la columna C» y lee el bloque en una
matriz Variant, lo procesa en memoria y lo reescribe en una sola asignación — con el recálculo y la
actualización de pantalla gestionados alrededor de la escritura. Indexa la matriz 2D correctamente, protege
el caso de una sola celda, y escribe .Value de forma explícita para que el código diga lo que quiere
decir. Tú describes la transformación; él escribe el viaje de ida y vuelta que corre en un parpadeo en
lugar del bucle que se arrastra.
Preguntas frecuentes
¿Cómo obtengo el valor de una celda en Excel VBA?
Lee la propiedad Value de un Range: x = Range("A1").Value o x = Cells(1, 1).Value. Para muchas
celdas a la vez, lee todo el rango en un Variant — arr = Range("A1:C100").Value — que te da una matriz
bidimensional de base 1 (arr(fila, columna)). Leer un bloque en una matriz es mucho más rápido que leer
cada celda en un bucle, porque cada acceso individual a .Value cruza de VBA a Excel.
¿Cómo escribo un valor en una celda en VBA?
Asigna a Value: Range("A1").Value = 42 o Cells(1, 1).Value = "Total". Para escribir muchas celdas a
la vez, construye una matriz Variant 2D y asígnala a un rango de la misma forma:
Range("A1:A100").Value = arr. Esto escribe las 100 celdas en una sola operación en lugar de recorrerlas
en un bucle, lo que es muchísimo más rápido en rangos grandes.
¿Por qué el valor de mi rango en VBA es una matriz 2D?
Porque un rango de varias celdas es una cuadrícula, así que su .Value es una matriz bidimensional
indexada como arr(fila, columna) y siempre de base 1 — incluso una sola columna es una matriz de N por 1,
así que la indexas arr(3, 1), no arr(3). Indexar con un solo subíndice lanza «Subscript out of range».
Una sola celda es la excepción: Range("A1").Value devuelve un escalar simple, no una matriz de 1 por 1.
¿Por qué es tan lento leer celdas en un bucle de VBA?
Cada acceso a Cells(i, j).Value cruza la frontera entre el motor de VBA y la aplicación Excel, y ese
salto es la parte cara — no la aritmética. Un bucle sobre 10.000 filas que lee y escribe cada celda hace
20.000 cruces. Leer el rango en una matriz, procesarlo en memoria y reescribir la matriz hace solo dos
cruces, y por eso a menudo es decenas de veces más rápido.
¿Cuál es la diferencia entre .Value y usar solo Range(...) en VBA?
.Value es la propiedad por defecto de un Range, así que x = Range("A1") se trata como
x = Range("A1").Value. Para los valores se leen igual, pero omitir .Value esconde tu intención:
Set rng = Range("A1") asigna el objeto rango, mientras que x = Range("A1") asigna su valor, y la
diferencia es solo la palabra clave Set. Escribir .Value de forma explícita hace que el código no sea
ambiguo.
Probado en
Probado en: Excel 365 (Windows 11), VBA 7.1 — última verificación el 31/08/2026.
Guías relacionadas: VBA Range · VBA Cells · VBA Formula · VBA Value vs Value2 vs Text · VBA Array
