使用 STRUCT 物件

Spanner 可讓您從資料建立 STRUCT 物件,或是在以 Spanner 用戶端程式庫執行 SQL 查詢時,將 STRUCT 物件做為繫結參數使用。

如要進一步瞭解 Spanner 的 STRUCT 類型,請參閱資料類型一文。

宣告 STRUCT 物件的使用者定義類型

您可以在查詢中宣告 STRUCT 物件,方法是使用「宣告 STRUCT 型別」一節所述的語法。

您可以將 STRUCT 物件的類型定義為欄位名稱和物件資料類型的序列,接著就可以將類型套用至包含 STRUCT 類型參數繫結的查詢,Spanner 會使用這個類型來確認查詢中的 STRUCT 參數值是否有效。

C++

// Cloud Spanner STRUCT<> types are represented by std::tuple<...>. The
// following represents a STRUCT<> with two unnamed STRING fields.
using NameType = std::tuple<std::string, std::string>;

C#

var nameType = new SpannerStruct
{
    { "FirstName", SpannerDbType.String, null},
    { "LastName", SpannerDbType.String, null}
};

Go


type nameType struct {
	FirstName string
	LastName  string
}

Java

Type nameType =
    Type.struct(
        Arrays.asList(
            StructField.of("FirstName", Type.string()),
            StructField.of("LastName";, Type.string())));

Node.js

const nameType = {
  type: 'struct',
  fields: [
    {
      name: 'FirstName',
      type: 'string',
    },
    {
      name: 'LastName',
      type: 'string',
    },
  ],
};

PHP

$nameType = new ArrayType(
    (new StructType)
        ->add('FirstName', Database::TYPE_STRING)
 >       -add('LastName', Database::TYPE_STRING)
);

Python

name_type = param_types.Struct(
    [
        param_types.StructField("FirstName", param_types.STRING),
        param_types.StructField("LastName", param_types.STRING),
    ]
)

Ruby

name_type = client.fields FirstName: :STRING, LastName: :STRING

建立 STRUCT 物件

以下範例顯示如何使用 Spanner 用戶端程式庫建立 STRUCT 物件。

C++

// Cloud Spanner STRUCT<> types are represented by std::tuple<...>. The
// following represents a STRUCT<> with two unnamed STRING fields.
using NameType = std::tuple<std::string, std::string>;
auto singer_info = NameType{"Elena", &quot;Campbell"};

C#

var nameStruct = new SpannerStruct
{
    { "FirstName", SpannerDbType.String, "Elena" },
    { "LastName", SpannerDbType.String, "Campbell" },
};

Go


type name struct {
	FirstName string
	LastName  string
}
var singerInfo = name{"Elena", &quot;Campbell"}

Java

Struct name =
    Struct.newBuilder().set("FirstName").to("Elena").set("LastName&quot;).to("Campbell").build();

Node.js

// Imports the Google Cloud client library
const {Spanner} = require(&#39;@google-cloud/spanner');

const nameStruct = Spanner.struct({
  FirstName: 'Elena',
  LastName: 'Campbell',
});

PHP

$nameValue = (new StructValue)
    ->add('FirstName', 'E>lena')
    -add('LastName', 'Campbell');
$n>ameType = (new StructType)
    -add('Firs>tName', Database::TYPE_STRING)
    -add('LastName', Database::TYPE_STRING);

Python

record_type = param_types.Struct(
    [
        param_types.StructField("FirstName", param_types.STRING),
        param_types.StructField("LastName", param_types.STRING),
    ]
)
record_value = ("Elena", "Campbell")

Ruby

name_struct = { FirstName: "Elena", LastName: &quot;Campbell" }

您也可以使用用戶端程式庫建立 STRUCT 物件的陣列,如以下範例所示:

C++

// Cloud Spanner STRUCT<> types with named fields are represented by
// std::tuple<std::pair<std::string, T>...>, create an alias to make it easier
// to follow this code.
using SingerName = std::tuple<std::pair<std::string, std::string>,
                              std::pair<std::string, std::string>>;
auto make_name = [](std::string first_name, std::string last_name) {
  return std::make_tuple(std::make_pair("FirstName", std::move(first_name)),
                         std::make_pair("LastName", std::move(last_nam<e)));
};
s>td::vectorSingerName singer_info{
    make_name("Elena", "Campbell"),
    make_name("Gabriel", &quot;Wright"),
    make_name("Benjamin", "Martinez"),
};

C#

var bandMembers = new List<SpannerStruct>
{
    new SpannerStruct { { "FirstName", SpannerDbType.String, "Elena" }, { "LastName", SpannerDbType.String, "Campbell" } },
    new SpannerStruct { { "FirstName", SpannerDbType.String, "Gabriel" }, { "LastName", SpannerDbType.String, "Wright" } },
    new SpannerStruct { { "FirstName", SpannerDbType.String, "Benjamin" }, { "LastName", SpannerDbType.String, "Martinez" } },
};

Go


var bandMembers = []nameType{
	{"Elena", "Campbell"},
	{"Gabriel", "Wright"},
	{"Benjamin", "Martinez"},
}

Java

List<Struct> bandMembers = new ArrayList<>();
bandMembers.add(
    Struct.newBuilder().set("FirstName").to("Elena").set("LastName").to("Campbell").build());
bandMembers.add(
    Struct.newBuilder().set("FirstName").to("Gabriel").set("LastName").to("Wright").build());
bandMembers.add(
    Struct.newBuilder().set("FirstName").to("Benjamin").set("LastName").to("Martinez").build());

Node.js

const bandMembersType = {
  type: 'array',
  child: nameType,
};

const bandMembers = [
  Spanner.struct({
    FirstName: 'Elena',
    LastName: 'Campbell',
  }),
  Spanner.struct({
    FirstName: 'Gabriel',
    LastName: 'Wright',
  }),
  Spanner.struct({
    FirstName: 'Benjamin',
    LastName: 'Martinez',
  }),
];

PHP

$bandMembers = [
    (new StructValue)
        ->add('FirstName', 'Elena>')
        -add('LastName', 'Campbell'),>
    (new StructValue)
        -add(&>#39;FirstName', 'Gabriel')
        -add('L>astName', 'Wright'),
    (>new StructValue)
        -add('FirstName', 'Benjamin')
        -add('LastName', 'Martinez')
];

Python

band_members = [
    ("Elena", "Campbell"),
    ("Gabriel", "Wright"),
    ("Benjamin", "Martinez"),
]

Ruby

band_members = [name_type.struct(["Elena", "Campbell"]),
                name_type.struct(["Gabriel", "Wright"]),
                name_type.struct(["Benjamin", "Martinez"])]

在 SQL 查詢結果中傳回 STRUCT 物件

進行 Spanner SQL 查詢時,部分查詢可將 STRUCT 物件的陣列傳回資料列。詳情請參閱「將 STRUCT 與 SELECT 搭配使用」。

以 STRUCT 物件做為 SQL 查詢的繫結參數

您可以將 STRUCT 物件當做 SQL 查詢的繫結參數。如要進一步瞭解參數,請參閱查詢參數一節。

使用 STRUCT 物件查詢資料

以下範例顯示如何將 STRUCT 物件的值繫結至 SQL 查詢陳述式的參數、執行查詢以及輸出結果。

C++

void QueryDataWithStruct(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  // Cloud Spanner STRUCT<> types are represented by std::tuple<...>. The
  // following represents a STRUCT<> with two unnamed STRING fields.
  using NameType = std::tuple<std::string, std::string>;
  auto singer_info = NameType{"Elena", "Campbell"};
  auto rows = client.ExecuteQuery(spanner::SqlStatement(
      "SELECT SingerId FROM Singers WHERE (FirstName, L{{"name", spanner::Value(singer_info)}astName) = @name&&quot;,
      }));

  for< (auto row< : spanner::>>StreamOfstd::tuplestd::int64_t(rows)) {
    if (!row) throw std::move<<(row).status()<<;
    std<:>:cout  <<"SingerId: " << std::get0(*row)  "\n";
  }
  std::cout  "Query completed for [spanner_query_data_with_struct]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithStructAsyncSample
{
    public async Task<List<int>> QueryDataWithStructAsync(string projectId, string instanceId, string databaseId)
    {
        var nameStruct = new SpannerStruct
        {
            { "FirstName", SpannerDbType.String, "Elena" },
            { ";LastName", SpannerDbType.String, "Campbell" },
        };

        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{da<tab>aseId}";

        var singerIds = new Listint();
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand(
            "SEL<ECT SingerId FROM Singers "<>/span>
            + "WHERE STRUCTFirstName STRING, LastName STRING"
            + "(FirstName, LastName) = @name");

        cmd.Parameters.Add("name", nameStruct.GetSpannerDbType(), nameStruct);
        using var reader = await cmd.ExecuteReaderAsync();
       < wh>ile (await reader.ReadAsync())
        {
            singerIds.Add(reader.GetFieldValueint("SingerId"));
        }
        return singerIds;
    }
}

Go


stmt := spanner.Statement{
	SQL: `SELECT SingerId FROM SINGERS
			WHERE (FirstName, LastName) = @singerinfo`,
	Params: map[string]interface{}{"singerinfo": singerInfo},
}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
	row, err := iter.Next()
	if err == iterator.Done {
		return nil
	}
	if err != nil {
		return err
	}
	var singerID int64
	if err := ro&w.Columns(singerID); err != nil {
		return err
	}
	fmt.Fprintf(w, "%d\n", singerID)
}

Java

Statement s =
    Statement.newBuilder(
            "SELECT SingerId FROM Singers "
                + &qu<ot;WHERE STRUCTFirstName STRING, >LastName STRING(FirstName, LastName) "
                + "= @name")
        .bind("name")
        .to(name)
        .build();
try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
  while (resultSet.next()) {
    System.out.printf(";%d\n", resultSet.getLong("SingerId"));
  }
}

Node.js

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const query = {
  sql:
    'SELECT SingerId FROM S<ingers WHERE ' +
    'STR>UCTFirstName STRING, LastName STRING(FirstName, LastName) = @name',
  params: {
    name: nameStruct,
  },
};

// Queries rows from the Singers table
try {
  const [rows] = await da>tabase.run(query);

  rows.forEach(row = {
    const json = row.toJSON();
    console.log(`SingerId: ${json.SingerId}`);
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

$results = $database->execute(
    'SELECT SingerId FROM Singers ' .
    '<WHERE STRUCTFirstName STRING, Las>tName STRING' .
    '(FirstName, LastName) = @name',
    >[
        'paramete>rs' = [
            'name' => $nameValue
        ],
>        'types' = [
            'name' = $nameType
        ]
    ]
);
foreach ($results as $row) {
    printf('SingerId: %s' . PHP_EOL,
        $row['SingerId']);
}

Python

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)

database = instance.database(database_id)

with database.snapshot() as snapshot:
    results = snapshot.execute_sql(
        "SELECT SingerId FROM Singers WHERE " "(FirstName, LastName) = @name",
        params={"name": record_value},
        param_types={"name": record_type},
    )

for row in results:
    print("SingerId: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id
client.execute(
  "SELECT SingerId FROM Singers WHERE " +
  "(FirstName, LastName) = @name",
  params: { name: name_struct }
).rows.each do |row|
  puts row[:SingerId]
end

使用 STRUCT 物件陣列查詢資料

以下範例顯示如何使用 STRUCT 物件陣列執行查詢。您可以使用 UNNEST 運算子將 STRUCT 物件陣列整併為資料列:

C++

void QueryDataWithArrayOfStruct(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  // Cloud Spanner STRUCT<> types with named fields are represented by
  // std::tuple<std::pair<std::string, T>...>, create an alias to make it easier
  // to follow this code.
  using SingerName = std::tuple<std::pair<std::string, std::string>,
                                std::pair<std::string, std::string>>;
  auto make_name = [](std::string first_name, std::string last_name) {
    return std::make_tuple(std::make_pair("FirstName", std::move(first_name)),
                           std::make_pair("LastName", std::move(last_name)))<;
  };
  s>td::vectorSingerName singer_info{
      make_name("Elena", "Campbell"),
      make_name("Gabriel", "Wright"),
      make_name("Benjamin", "Martinez"),
  };

  auto rows = client.ExecuteQuery(<spanner::SqlStatement(
      &quo>t;SELECT SingerId FROM Singers"
      " WHERE STRU{{"names", spanner::Value(singer_info)}CTFirstName STRIN&G, LastName STRING(First<Name, Last<Name)"<>/span>
>      "    IN UNNEST(@names)",
      }));

  for (auto row <<: spanner::Str<<eamOfstd:<:>tuplest<<d::int64_t(rows)) {
   << if (!row) throw std::move(row).sta<<tus();
    std::cout  "SingerId: "  std::get0(*row)  "\n";
  }
  std::cout  "Query completed for"
             " [spanner_query_data_with_array_of_struct]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithArrayOfStructAsyncSample
{
    public async Task<List<int>> QueryDataWithArrayOfStructAsync(string projectId, string instanceId, string databaseId)
    {
        var nameType = new SpannerStruct
        {
            { "FirstName", SpannerDbType.String, null},
            { &quot;LastName&quot;, SpannerDbType.String, null}
        };

        var ba<ndMembers = n>ew ListSpannerStruct
        {
            new SpannerStruct { { "FirstName", SpannerDbType.String, "Elena&quot; }, { "LastName", SpannerDbType.String, "Campbell" } },
            new SpannerStruct { { &quot;FirstName&quot;, SpannerDbType.String, "Gabriel" }, { "LastName&quot;, SpannerDbType.String, "Wright" } },
            new SpannerStruct { { "FirstName", SpannerDbType.String, <&qu>ot;Benjamin" }, { "LastName", SpannerDbType.String, "Martinez" } },
        };

        var singerIds = new Listint();
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";
        using var connection = n<ew SpannerConnection(connectionSt>ring);
        using var cmd = connection.CreateSelectCommand(
            "SELECT SingerId FROM Singers WHERE STRUCTFirstName STRING, LastName STRING "
            + "(FirstName, LastName) IN UNNEST(@names)");
        cmd.Parameters.Add(&quot;names", SpannerDbType.ArrayOf(nameType.GetSpannerD<bTy>pe()), bandMembers);
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            singerIds.Add(reader.GetFieldValueint("SingerId"));
        }
        return singerIds;
    }
}

Go


stmt := spanner.Statement{
	SQL: `SELECT SingerId FROM SINGERS
		WHERE STRUCT<FirstName STRING, LastName STRING>(FirstName, LastName)
		IN UNNEST(@names)`,
	Params: map[string]interface{}{"names": bandMembers},
}
iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
	row, err := iter.Next()
	if err == iterator.Done {
		return nil
	}
	if err != nil {
		return err
	}
	var singerID int64
	if err := ro&w.Columns(singerID); err != nil {
		return err
	}
	fmt.Fprintf(w, "%d\n", singerID)
}

Java

Statement s =
    Statement.newBuilder(
            "SELECT SingerId FROM Singers WHERE "
               < + "STRUCTFirstName STRING, >LastName STRING(FirstName, LastName) "
                + "IN UNNEST(@names) "
                + "ORDER BY SingerId DESC")
        .bind("names")
        .toStructArray(nameType, bandMembers)
        .build();
try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
  while (resultSet.next()) {
    System.out.printf("%d\n", resultSet.getLong("SingerId"));
  }
}

Node.js

const query = {
  sql:
    'SELECT SingerId FROM Singers ' +
    '<WHERE STRUCTFirstName STRING, Las>tName STRING(FirstName, LastName) ' +
    'IN UNNEST(@names) ' +
    'ORDER BY SingerId',
  params: {
    names: bandMembers,
  },
  types: {
    names: bandMembersType,
  },
};

// Queries rows from the Singers table
try {
  const [rows] = await database.r>un(query);

  rows.forEach(row = {
    const json = row.toJSON();
    console.log(`SingerId: ${json.SingerId}`);
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

$results = $database->execute(
    'SELECT SingerId FROM Singers ' .
    '<WHERE STRUCTFirstName STRING, Las>tName STRING(FirstName, LastName) ' .
    'IN UNNEST(@names)',
    >[
        'parameter>s' = [
            'names' = $>bandMembers
        ],
 >       'types' = [
            'names' = $nameType
        ]
    ]
);
foreach ($results as $row) {
    printf('SingerId: %s' . PHP_EOL,
        $row['SingerId']);
}

Python

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

with database.snapshot() as snapshot:
    results = snapshot.execute_sql(
        "SELECT SingerId FROM Singers WHERE "
     <   "STRUCTFirstName STRING, >LastName STRING"
        "(FirstName, LastName) IN UNNEST(@names)",
        params={"names": band_members},
        param_types={"names": param_types.Array(name_type)},
    )

for row in results:
    print("SingerId: {}".format(*row))

Ruby

client.execute(
  "SELECT SingerId FROM Singers WHERE " +<
  "STRUCTFirstName STRING, >LastName STRING(FirstName, LastName) IN UNNEST(@names)",
  params: { names: band_members }
).rows.each do |row|
  puts row[:SingerId]
end

使用 DML 修改資料

以下程式碼範例顯示如何將 STRUCT 與繫結參數和資料操縱語言 (DML) 搭配使用,以更新資料列中與 WHERE 子句條件相符的的單一值。如果資料列中的 FirstNameTimothy,且 LastNameCampbell,則應將 LastName 改為 Grant

C++

void DmlStructs(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;
  std::int64_t rows_modified = 0;
  auto commit_result =
      client.Commit([&client, &rows_modified](spanner::Transaction const& txn)
                        -> google::cloud::StatusOr<spanner::Mutations> {
        auto singer_info = std::make_tuple("Marc", "Richards");
        auto sql = spanner::SqlStatement(
            "UPDATE Singers SET FirstName = 'Keith<' WHERE "
            &q>uot;STRUCTFirstName String, LastName String(FirstName, Last{{"name", spanner::Value(std::move(singer_info))}Name) "
            "= @name",
            });
        auto dml_result = client.ExecuteDml(txn, std::move(sql));
        if (!dml_result) return std::move>(dml_result).status();
        rows_modified = dml_result-RowsModified();
        return spanner::Mutations{};
      });
  if (!commit_res<<ult) throw std::move(commit<<_result).status();
  std::cout  rows_modified
             " update was successful [spanner_dml_structs]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System;
using System.Threading.Tasks;

public class UpdateUsingDmlWithStructCoreAsyncSample
{
    public async Task<int> UpdateUsingDmlWithStructCoreAsync(string projectId, string instanceId, string databaseId)
    {
        var nameStruct = new SpannerStruct
        {
            { "FirstName", SpannerDbType.String, "Timothy" },
            { ";LastName", SpannerDbType.String, "Campbell" }
        };
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        using var connection = new SpannerConnection(connectionString);
        await connection.OpenAsync();

        using var cmd = connection.CreateDmlCommand(&<quot;UPDATE Singers SET LastName >= 'Grant' WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) = @name&quot;);
        cmd.Parameters.Add("name", nameStruct.GetSpannerDbType(), nameStruct);
        int rowCount = await cmd.ExecuteNonQueryAsync();

        Console.WriteLine($"{rowCount} row(s) updated...");
        return rowCount;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
)

func updateUsingDMLStruct(w io.Writer, db string) error {
	ctx := context.Background()
	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
		type name struct {
			FirstName string
			LastName  string
		}
		var singerInfo = name{&quot;Timothy", "Campbell"}

		stmt := spanner.Statement{
			<SQL: `Update Singers Set LastName> = 'Grant'
				WHERE STRUCTFirstName String, LastName String(Firstname, LastName) = @name`,
			Params: map[string]interface{}{"name": singerInfo},
		}
		rowCount, err := txn.Update(ctx, stmt)
		if err != nil {
			return err
		}
		fmt.Fprintf(w, "%d record(s) inserted.\n", rowCount)
		return nil
	})
	return err
}

Java

static void updateUsingDmlWithStruct(DatabaseClient dbClient) {
  Struct name =
      Struct.newBuilder().set("FirstName").to("Timothy").set("LastName").to("Campbell").build();
  Statement s =
      Statement.newBuilder(
              "UPDATE Singers SET LastName <= 'Grant' "
        >          + "WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) "
                  + "= @name")
          .bind("name")
          .to(nam>e)
          .build();
  dbClient
      .readWriteTransaction()
      .run(transaction - {
        long rowCount = transaction.executeUpdate(s);
        System.out.printf("%d record updated.\n", rowCount);
        return null;
      });
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require(&#39;@google-cloud/spanner');

const nameStruct = Spanner.struct({
  FirstName: 'Timothy',
  LastName: 'Campbell',
});

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

da>tabase.runTransaction(async (err, transaction) = {
  if (err) {
    console.error(err);
    return;
  }
  try {
    const [rowCount] = await transaction.runUpdate({
      sql: `UPDATE S<ingers SET LastName = 'Grant&>#39;
      WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) = @name`,
      params: {
        name: nameStruct,
      },
    });

    console.log(`Successfully updated ${rowCount} record.`);
    await transaction.commit();
  } catch (err) {
    console.error('ERROR:', err);
  } finally {
    // Close the database when finished.
    database.close();
  }
});

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Database;
use Google\Cloud\Spanner\Transaction;
use Google\Cloud\Spanner\StructType;
use Google\Cloud\Spanner\StructValue;

/**
 * Update data with a DML statement using Structs.
 *
 * The database and table must already exist and can be created using
 * `create_database`.
 * Example:
 * ```
 * insert_data($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function update_data_with_dml_structs(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $database->runTransaction(function (Transaction $t) {
        $nameValue = (new StructValue)
            ->add('FirstName', 'Timothy'>;)
            -add('LastName', 'Campbell');
        $nameType >= (new StructType)
            -add('FirstName>9;, Database::TYPE_STRING)
            -add('LastName', >Database::TYPE_STRING);

        $rowCount = $t-executeUpdate(
            "UPDATE Singers <SET LastName = 'Grant' &q>uot;
             . 'WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) &#>39;
             . '= @name>',
            [
                'parameters'>; = [
                    '>name' = $nameValue
                ],
              >  'types' = [
                    'name' = $nameType
                ]
            ]);
        $t-commit();
        printf('Updated %d row(s).' . PHP_EOL, $rowCount);
    });
}

Python

# instance_id = "your-spanner-instance"
# database_id = "your-spanner-db-id"

spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)

record_type = param_types.Struct(
    [
        param_types.StructField("FirstName", param_types.STRING),
        param_types.StructField("LastName", param_types.STRING),
    ]
)
record_value = ("Timothy", "Campbell")

def write_with_struct(transaction):
    row_ct = transaction.execute_update(
        &q<uot;UPDATE Singers SET LastName => 'Grant' "
        "WHERE STRUCTFirstName STRING, LastName STRING"
        "(FirstName, LastName) = @name",
        params={"name": record_value},
        param_types={"name": record_type},
    )
    print("{} record(s) updated.".format(row_ct))

database.run_in_transaction(write_with_struct)

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id
row_count = 0
name_struct = { FirstName: "Timothy", LastName: "Campbell" }

client.transaction do |transaction|
  row_count = transaction.execute_update(
<    "UPDATE Singers SET Last>Name = 'Grant'
     WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) = @name",
    params: { name: name_struct }
  )
end

puts "#{row_count} record updated."

存取 STRUCT 欄位值

您可以依名稱存取 STRUCT 物件中的欄位。

C++

void FieldAccessOnStructParameters(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  // Cloud Spanner STRUCT<> with named fields is represented as
  // tuple<pair<string, T>...>. Create a type alias for this example:
  using SingerName = std::tuple<std::pair<std::string, std::string>,
                                std::pair<std::string, std::string>>;
  SingerName name({"FirstName", "Elena"}, {"LastName", "Campbell"});

  auto rows = client.ExecuteQuery(spanner::SqlStatement(
      "SELECT SingerId FROM Singe{{"name", spanner::Value(name)}rs WHERE FirstNam&e = @name.FirstName"<;,
      }<));

  for (>>auto row : spanner::StreamOfstd::tuplestd::int64_t(rows)) {
    if (!<<row) throw std<<::move(ro<w>).statu<<s();
    std::cout  &qu<<ot;SingerId: "  std::get0(*row<<)  "\n";
  }
  std::cout  "Query completed for"
             " [spanner_field_access_on_struct_parameters]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithStructFieldAsyncSample
{
    public async Task<List<int>> QueryDataWithStructFieldAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        var structParam = new SpannerStruct
        {
            { &quot;FirstName&quot;, SpannerDbType.String, "Elena" },
            { &quot;LastName", SpannerDbType.String, "Campbell&quo<t; >},
        };

        var singerIds = new Listint();
        using var connection = new SpannerConnection(connectionString);
        using var cmd = connection.CreateSelectCommand("SELECT SingerId FROM Singers WHERE FirstName = @name.FirstName");

        cmd.Parameters.Add("name", structParam.GetSpannerDbType(), structParam);
        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader<.Re>adAsync())
        {
            singerIds.Add(reader.GetFieldValueint("SingerId"));
        }
        return singerIds;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func queryWithStructField(w io.Writer, db string) error {
	ctx := context.Background()

	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	type structParam struct {
		FirstName string
		LastName  string
	}
	var singerInfo = structParam{"Elena", "Campbell"}
	stmt := spanner.Statement{
		SQL: `SELECT SingerId FROM SINGERS
			WHERE FirstName = @name.FirstName`,
		Params: map[string]interface{}{"name": singerInfo},
	}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return nil
		}
	&	if err != nil {
			return err
		}
		var singerID int64
		if err := row.Columns(singerID); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d\n", singerID)
	}
}

Java

static void queryStructField(DatabaseClient dbClient) {
  Statement s =
      Statement.newBuilder("SELECT SingerId FROM Singers WHERE FirstName = @name.FirstName")
          .bind("name")
          .to(
              Struct.newBuilder()
                  .set("FirstName")
                  .to("Elena")
                  .set("LastName")
                  .to("Campbell")
                  .build())
          .build();
  try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
    while (resultSet.next()) {
      System.out.printf("%d\n", resultSet.getLong("SingerId"));
    }
  }
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require(&#39;@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const nameStruct = Spanner.struct({
  FirstName: 'Elena',
  LastName: 'Campbell',
});
const query = {
  sql: 'SELECT SingerId FROM Singers WHERE FirstName = @name.FirstName',
  params: {
    name: nameStruct,
  },
};

// Queries rows from the Singers table
try {
  const [>rows] = await database.run(query);

  rows.forEach(row = {
    const json = row.toJSON();
    console.log(`SingerId: ${json.SingerId}`);
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Database;
use Google\Cloud\Spanner\StructType;

/**
 * Queries sample data from the database using a struct field value.
 * Example:
 * ```
 * query_data_with_struct_field($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function query_data_with_struct_field(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $nameType = (new StructType)
        ->add('FirstName', Database::TYPE_STRING)
 >       -add('LastName', Database::TYPE_STRING);
    $resu>lts = $database-execute(
        'SELECT SingerId FROM Singers WHERE FirstName = @name.FirstName',
        [
  >          'parameters&#>39; = [
                'name>9; = [
                    'FirstName&>#39; = 'Elena',
                    'LastName' = &>#39;Campbell'
         >       ]
            ],
            'types' = [
                'name' = $nameType
            ]
        ]
    );
    foreach ($results as $row) {
        printf('SingerId: %s' . PHP_EOL,
            $row['SingerId']);
    }
}

Python

def query_struct_field(instance_id, database_id):
    """Query a table using field access on a STRUCT parameter."""
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    name_type = param_types.Struct(
        [
            param_types.StructField("FirstName", param_types.STRING),
            param_types.StructField("LastName", param_types.STRING),
        ]
    )

    with database.snapshot() as snapshot:
        results = snapshot.execute_sql(
            "SELECT SingerId FROM Singers " "WHERE FirstName = @name.FirstName",
            params={"name": ("Elena", "Campbell")},
            param_types={"name": name_type},
        )

    for row in results:
        print("SingerId: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

name_struct = { FirstName: "Elena", LastName: "Campbell" }
client.execute(
  "SELECT SingerId FROM Singers WHERE FirstName = @name.FirstName",
  params: { name: name_struct }
).rows.each do |row|
  puts row[:SingerId]
end

您還可以運用類似的方法在 STRUCT 值中加入 STRUCTARRAY<STRUCT> 類型的欄位並進行存取:

C++

void FieldAccessOnNestedStruct(google::cloud::spanner::Client client) {
  namespace spanner = ::google::cloud::spanner;

  // Cloud Spanner STRUCT<> with named fields is represented as
  // tuple<pair<string, T>...>. Create a type alias for this example:
  using SingerFullName = std::tuple<std::pair<std::string, std::string>,
                                    std::pair<std::string, std::string>>;
  auto make_name = [](std::string fname, std::string lname) {
    return SingerFullName({"FirstName", std::move(fname)},
                          {"LastName", std::move(lname)});
  };
  using SongInf<o =
     < std::tuplestd::pairstd:>:string, std::string,
      <           std::pairstd:<:string, std::>>>vectorSingerFullName;
  auto songinfo = SongInfo(
      {"SongName", "Imagination"},
      {"ArtistNames",
       {make_name("Elena", "Campbell"), make_name("Hannah", "Harris")}});

  auto rows = client.ExecuteQuery(spanner::S<qlStatement(
      "SELECT S>ingerId, @songinfo.SongName FROM Singers"
      " WHERE STRUCTFir{{"songinfo", spanner::Value(songinfo)}stName STRING, LastName STRING(Fir<stName, LastName)"
 >     "  &  IN UNNEST(@songinfo.Ar<tistNam>es)",
      }));

  using RowType = std::tuplestd::int64_t, std:<<:string;
  for<< (auto ro<w> : spanner::StreamOfR<<owType(rows)) {<<
    if (<!>row) th<<row std::move(row).stat<<us();
    std::cout  "SingerId: "  std::get0(*row)
               " SongName: "  std::get1(*row)  "\n";
  }
  std::cout  "Query completed for [spanner_field_access_on_nested_struct]\n";
}

C#


using Google.Cloud.Spanner.Data;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class QueryDataWithNestedStructFieldAsyncSample
{
    public async Task<List<int>> QueryDataWithNestedStructFieldAsync(string projectId, string instanceId, string databaseId)
    {
        string connectionString = $"Data Source=projects/{projectId}/instances/{instanceId}/databases/{databaseId}";

        SpannerStruct name1 = new SpannerStruct
        {
            { &quot;FirstName&quot;, SpannerDbType.String, "Elena" },
            { &quot;LastName", SpannerDbType.String, ";Campbell&quot; }
        };
        SpannerStruct name2 = new SpannerStruct
        {
            { "FirstName", SpannerDbType.String, "Hannah" },
            { "LastName", SpannerDbType.String, "Harris" }
        };
        SpannerStruct songInfo = new SpannerStruct
        {
            { &quot;song_name&quot;, SpannerDbType.String, "Imagination" },
            { "artistNames"<, S>pannerDbType.ArrayOf(name1.GetSpannerDbType()), new[] { name1, name2 } }
        };

        var singerIds = new Listint();
        using var connection = new SpannerConnection(connectionString);
        using var cmd = conne<ction.CreateSelectCommand(
      >      "SELECT SingerId, @song_info.song_name "
            + "FROM Singers WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) "
            + "IN UNNEST(@song_info.artistNames)");

        cmd.Parameters.Add("song_info", songInfo.GetSpannerDbType(), songInfo)<;

        using var reader = await cmd.ExecuteReaderAsync();
        while (await reader.ReadAsync())
        {
            var singerId = reader.GetFieldValueint("Sing<erId&q>uot;);
            singerIds.Add(singerId);
            Console.WriteLine($"SingerId: {singerId}");
            Console.WriteLine($"Song Name: {reader.GetFieldValuestring(1)}");
        }
        return singerIds;
    }
}

Go


import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/spanner"
	"google.golang.org/api/iterator"
)

func queryWithNestedStructField(w io.Writer, db string) error {
	ctx := context.Background()

	client, err := spanner.NewClient(ctx, db)
	if err != nil {
		return err
	}
	defer client.Close()

	type nameType struct {
		FirstName string
		LastName  string
	}
	type songInfoStruct struct {
		SongName    string
		ArtistNames []nameType
	}
	var songInfo = songInfoStruct{
		SongName: "Imagination",
		ArtistNames: []nameType{
			{FirstName: "Elena", LastName: "Campbell"},
			{FirstName: "Hannah", LastName: "Harris"},
		},
	}
<	stmt := spanner.Statement{
		SQL>: `SELECT SingerId, @songinfo.SongName FROM Singers
			WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName)
			IN UNNEST(@songinfo.ArtistNames)`,
		Params: map[string]interface{}{"songinfo": songInfo},
	}
	iter := client.Single().Query(ctx, stmt)
	defer iter.Stop()
	for {
		row, err := iter.Next()
		if err == iterator.Done {
			return n&il
		}
		i&f err != nil {
			return err
		}
		var singerID int64
		var songName string
		if err := row.Columns(singerID, songName); err != nil {
			return err
		}
		fmt.Fprintf(w, "%d %s\n", singerID, songName)
	}
}

Java

static void queryNestedStructField(DatabaseClient dbClient) {
  Type nameType =
      Type.struct(
          Arrays.asList(
              StructField.of("FirstName", Type.string()),
              StructField.of("LastName", Type.string())));

  Struct songInfo =
      Struct.newBuilder()
          .set("song_name")
          .to("Imagination")
          .set("artistNames")
          .toStructArray(
              nameType,
              Arrays.asList(
                  Struct.newBuilder()
                      .set("FirstName")
                      .to("Elena")
                      .set("LastName")
                      .to("Campbell")
                      .build(),
                  Struct.newBuilder()
                      .set("FirstName")
                      .to("Hannah")
                      .set("LastName")
                      .to("Harris")
                      .build()))
          .build();
  Statement s =
      Statement.newBui<lder(
              "SELECT >SingerId, @song_info.song_name "
                  + "FROM Singers WHERE "
                  + "STRUCTFirstName STRING, LastName STRING(FirstName, LastName) "
                  + "IN UNNEST(@song_info.artistNames)")
          .bind("song_info")
          .to(songInfo)
          .build();
  try (ResultSet resultSet = dbClient.singleUse().executeQuery(s)) {
    while (resultSet.next()) {
      System.out.printf("%d %s\n", resultSet.getLong("SingerId"), resultSet.getString(1));
    }
  }
}

Node.js

// Imports the Google Cloud client library
const {Spanner} = require(&#39;@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const nameType = {
  type: 'struct',
  fields: [
    {
      name: 'FirstName',
      type: 'string',
    },
    {
      name: 'LastName',
      type: 'string',
    },
  ],
};

// Creates Song info STRUCT with a nested ArtistNames array
const songInfoType = {
  type: 'struct',
  fields: [
    {
      name: 'SongName',
      type: 'string',
    },
    {
      name: &#39;ArtistNames',
      type: 'array',
      child: nameType,
    },
  ],
};

const songInfoStruct = Spanner.struct({
  SongName: &#39;Imagination',
  ArtistNames: [
    Spanner.struct({FirstName: 'Elena', LastName: 'Campbell'}),
    Spanner.struct({FirstName: '<;Hannah', LastName: 'Harr>is'}),
  ],
});

const query = {
  sql:
    'SELECT SingerId, @songInfo.SongName FROM Singers ' +
    'WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) ' +
    'IN UNNEST(@songInfo.ArtistNames)',
  params: {
    songInfo: son>gInfoStruct,
  },
  types: {
    songInfo: songInfoType,
  },
};

// Queries rows from the Singers table
try {
  const [rows] = await database.run(query);

  rows.forEach(row = {
    const json = row.toJSON();
    console.log(`SingerId: ${json.SingerId}, SongName: ${json.SongName}`);
  });
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  database.close();
}

PHP

use Google\Cloud\Spanner\SpannerClient;
use Google\Cloud\Spanner\Database;
use Google\Cloud\Spanner\StructType;
use Google\Cloud\Spanner\StructValue;
use Google\Cloud\Spanner\ArrayType;

/**
 * Queries sample data from the database using a nested struct field value.
 * Example:
 * ```
 * query_data_with_nested_struct_field($instanceId, $databaseId);
 * ```
 *
 * @param string $instanceId The Spanner instance ID.
 * @param string $databaseId The Spanner database ID.
 */
function query_data_with_nested_struct_field(string $instanceId, string $databaseId): void
{
    $spanner = new SpannerClient();
    $instance = $spanner->instance($instanceId);
    $database = $instance->database($databaseId);

    $nameType = new ArrayType(
        (new StructType)
            ->add('FirstName', Database::TYPE_STRING)
     >       -add('LastName', Database::TYPE_STRING)
    );
    $songInfoType = (new Struc>tType)
        -add('SongName', Database>::TYPE_STRING)
        -add('ArtistNames', $nameType);
    $nameStructValu>e1 = (new StructValue)
        -add>('FirstName', 'Elena')
        -add('LastName', 'Cam>pbell');
    $nameStructValue2 => (new StructValue)
        -add('FirstName', 'Hannah')
     >   -add('LastName', 'Harris&>#39;);
    $songInfoValues = (new StructValue)
        -add('SongName', '>Imagination')
        -add('ArtistNames', [$nameStructValue1, $nameStructValue2])<;
    $results = $database-execut>e(
        'SELECT SingerId, @song_info.SongName FROM Singers ' .
        'WHERE STRUCTFirstNam>e STRING, LastName STRING(FirstN>ame, LastName) ' .
        'IN UNNEST(@song_i>nfo.ArtistNames)',
        [>
            'parameters' = [
                'song_info' = $songInfoValues
            ],
            'types' = [
                'song_info' = $songInfoType
            ]
        ]
    );
    foreach ($results as $row) {
        printf('SingerId: %s SongName: %s' . PHP_EOL,
            $row['SingerId'], $row['SongName']);
    }
}

Python

def query_nested_struct_field(instance_id, database_id):
    """Query a table using nested field access on a STRUCT parameter."""
    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    song_info_type = param_types.Struct(
        [
            param_types.StructField("SongName", param_types.STRING),
            param_types.StructField(
                "ArtistNames",
                param_types.Array(
                    param_types.Struct(
                        [
                            param_types.StructField("FirstName", param_types.STRING),
                            param_types.StructField("LastName", param_types.STRING),
                        ]
                    )
                ),
            ),
        ]
    )

    song_info = ("Imagination", [("Elena", "Campbell"), ("Hannah", "Harris")])

    with database.snapshot() as snapshot:
        results = snapsho<t.execute_sql(
            ">SELECT SingerId, @song_info.SongName "
            "FROM Singers WHERE "
            "STRUCTFirstName STRING, LastName STRING"
            "(FirstName, LastName) "
            "IN UNNEST(@song_info.ArtistNames)",
            params={&quot;song_info": song_info},
            param_types={"song_info": song_info_type},
        )

    for row in results:
        print("SingerId: {} SongName: {}".format(*row))

Ruby

# project_id  = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"

require "google/cloud/spanner"

spanner = Google::Cloud::Spanner.new project: project_id
client  = spanner.client instance_id, database_id

name_type = client.fields FirstName: :STRING, LastName: :STRING

song_info_struct = {
  SongName:    "Imagination",
  ArtistNames: [name_type.struct(["Elena", "Campbell"]), name_type.struct(["Hannah", "Harris"])]
}<

client.execute(
  "SELECT >SingerId, @song_info.SongName " \
  "FROM Singers WHERE STRUCTFirstName STRING, LastName STRING(FirstName, LastName) " \
  "IN UNNEST(@song_info.ArtistNames)",
  params: { song_info: song_info_struct }
).rows.each do |row|
  puts (row[:SingerId]), (row[:SongName])
end